Skip to content

Commit

Permalink
feat: introduces usePii hook and withPii HOC to pass vendor-specific …
Browse files Browse the repository at this point in the history
…masking HTML attributes
  • Loading branch information
adamstankiewicz committed Aug 13, 2024
1 parent c878cce commit 148934c
Show file tree
Hide file tree
Showing 10 changed files with 499 additions and 199 deletions.
9 changes: 9 additions & 0 deletions env.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,15 @@
// Also note that in an actual application this file would be added to .gitignore.
const config = {
JS_FILE_VAR: 'JS_FILE_VAR_VALUE_FOR_EXAMPLE_APP',
piiOptions: {
selectors: {
username: [
{ attributeName: 'data-dd-privacy', attributeValue: 'mask' }, // Datadog example
{ attributeName: 'data-hj-suppress', attributeValue: '' }, // Hotjar example
{ className: 'fs-mask' }, // Fullstory example
],
},
},
};

export default config;
1 change: 1 addition & 0 deletions example/ExamplePage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ class ExamplePage extends Component {
<p>EXAMPLE_VAR env var came through: <strong>{getConfig().EXAMPLE_VAR}</strong></p>
<p>JS_FILE_VAR var came through: <strong>{getConfig().JS_FILE_VAR}</strong></p>
<p>Visit <Link to="/authenticated">authenticated page</Link>.</p>
<p>Visit <Link to="/pii">PII page</Link>.</p>
<p>Visit <Link to="/error_example">error page</Link>.</p>
</div>
);
Expand Down
95 changes: 95 additions & 0 deletions example/PiiPage.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import React, {
forwardRef, useContext, useEffect, useRef, useState,
} from 'react';
import PropTypes from 'prop-types';

import { AppContext, usePii, withPii } from '@edx/frontend-platform/react';

// Example via `usePii` (hook)
const UsernameWithPii = forwardRef(({ children, ...rest }, ref) => {
const piiProps = usePii('username', rest);
return <span ref={ref} {...piiProps}>{children}</span>;
});
UsernameWithPii.displayName = 'UsernameWithPii';
UsernameWithPii.propTypes = {
children: PropTypes.node.isRequired,
};

// Example via `withPii` (HOC)
const Username = forwardRef(({ children, ...rest }, ref) => (
<span ref={ref} {...rest}>{children}</span>
));
Username.displayName = 'Username';
Username.propTypes = {
children: PropTypes.node.isRequired,
};
const UsernameWithPii2 = withPii('username')(Username);

export default function PiiPage() {
const { authenticatedUser, config } = useContext(AppContext);
const firstUsernameRef = useRef(null);
const secondUsernameRef = useRef(null);
const [firstUsernameNode, setFirstUsernameNode] = useState(null);
const [secondUsernameNode, setSecondUsernameNode] = useState(null);

useEffect(() => {
if (firstUsernameRef.current) {
setFirstUsernameNode(firstUsernameRef.current.outerHTML);
}
if (secondUsernameRef.current) {
setSecondUsernameNode(secondUsernameRef.current.outerHTML);
}
}, []);

const { piiOptions } = config;
return (
<div>
<h1>Privacy options to obfuscate PII</h1>

<h2>Current PII configuration</h2>
{piiOptions ? (
<pre>
{JSON.stringify(piiOptions, null, 2)}
</pre>
) : (
<p>
No PII-related configuration found. Consider updating this application&apos;s <code>env.config.js</code> to
configure any vendor-specific PII-related attributes/values.
</p>
)}

<h2>Examples</h2>
<p>
The following examples below demonstrate using <code>usePii</code> and <code>withPii</code> to
obfuscate PII (e.g., username) based on the application&apos;s configuration. Inspect the DOM
elements for the rendered usernames below to observe the configured PII attributes/values.
</p>

{/* Example 1 (usePii) */}
<h3><code>usePii</code> (hook)</h3>
<p>
<i>Obfuscated username:</i>{' '}
<UsernameWithPii ref={firstUsernameRef}>
{authenticatedUser.username}
</UsernameWithPii>
</p>
<i>Result:</i>{' '}
<pre>
<code>{firstUsernameNode}</code>
</pre>

{/* Example 2 (withPii) */}
<h3><code>withPii</code> (HOC)</h3>
<p>
<i>Obfuscated username:</i>{' '}
<UsernameWithPii2 ref={secondUsernameRef}>
{authenticatedUser.username}
</UsernameWithPii2>
</p>
<i>Result:</i>{' '}
<pre>
<code>{secondUsernameNode}</code>
</pre>
</div>
);
}
2 changes: 2 additions & 0 deletions example/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { Routes, Route } from 'react-router-dom';
import './index.scss';
import ExamplePage from './ExamplePage';
import AuthenticatedPage from './AuthenticatedPage';
import PiiPage from './PiiPage';

subscribe(APP_READY, () => {
ReactDOM.render(
Expand All @@ -27,6 +28,7 @@ subscribe(APP_READY, () => {
element={<PageWrap><ErrorPage message="Test error message" /></PageWrap>}
/>
<Route path="/authenticated" element={<AuthenticatedPageRoute><AuthenticatedPage /></AuthenticatedPageRoute>} />
<Route path="/pii" element={<AuthenticatedPageRoute><PiiPage /></AuthenticatedPageRoute>} />
</Routes>
</AppProvider>,
document.getElementById('root'),
Expand Down
Loading

0 comments on commit 148934c

Please sign in to comment.