diff --git a/packages/access/README.md b/packages/access/README.md new file mode 100644 index 0000000..5afc5da --- /dev/null +++ b/packages/access/README.md @@ -0,0 +1,82 @@ + +### CheckAccessProvider +A simple provider based on the Redux library, used to store access to components and application pages + +Props: + +| Property | type | Description | +| --------------- | --------------------- | --------------------- | +| userLevelSelector | Integer | Selector that returns integer value for comparison with the access flag | + +Usage: + +```javascript +import { Provider } from 'react-redux' +import CheckAccessProvider from '@ds-frontend/access' + +import userLevelSelector from '...path' + +function App(){ + return ( + + + // ...childrens + + + ) +} +``` +Than you need to create **userLevelSelector** file. This file should describe the logic of all permissions for each role. For this we prefer to use the **reselect** library + +```javascript +import isEmpty from 'lodash/isEmpty' +import get from 'lodash/get' +import { createSelector } from 'reselect' + +export const F_PUBLIC = 2 ** 0 +export const F_PROTECTED = 2 ** 1 +export const F_UNAUTHORISED = 2 ** 2 +export const F_CHIEF = 2 ** 52 - 1 + +export const userLevelSelector = createSelector( + // base permissions + (state) => isEmpty(get(state, 'session.data')) ? F_UNAUTHORISED : F_PROTECTED, + + // collect all user permissions + (...args) => args.reduce((level, flag) => level | flag, F_PUBLIC), +) + +``` +*NOTE: F_CHIEF have full access to application. It should contains all flags. the value should be next exponent minus one. The maximum exponent can be 52, because the MAX_SAFE_INTEGER is (2 ** 53)* + +### CheckAccess +React component (Consumer) for condition rendering + +Props: + +| Property | type | Description | +| --------------- | --------------------- | --------------------- | +| access | Integer | Acess Level | +| fallback | React Element | React Componet to render if condition is `false` | +| children | Node | Will render children component if condition is `true` | + +Usage: + +```javascript +import { CheckAccess } from '@ds-frontend/access' + +import { F_PROTECTED } from 'path/userLevelSelector' + + +export default function SessionText() { + return ( + Unauthorised} + > + Authorised + + ) +} +``` +Since our flag depends on whether our session is active or not, a text `Authorised` will be displayed during an active session. Otherwise, the text `Unauthorised` will be displayed diff --git a/packages/access/index.js b/packages/access/index.js new file mode 100644 index 0000000..fca20b9 --- /dev/null +++ b/packages/access/index.js @@ -0,0 +1,4 @@ +import CheckAccessProvider, { CheckAccess } from './src/CheckAccess' + + +export { CheckAccess, CheckAccessProvider } diff --git a/packages/access/package.json b/packages/access/package.json new file mode 100644 index 0000000..509780e --- /dev/null +++ b/packages/access/package.json @@ -0,0 +1,21 @@ +{ + "name": "@ds-frontend/access", + "version": "2.0.0-alpha", + "description": "React component for condition rendering", + "keywords": [ + "react", + "redux", + "access" + ], + "author": "DjangoStars (https://github.com/django-stars/)", + "contributors": [ + "Nikita Mazur " + ], + "dependencies": { + "prop-types": "^15.7.2" + }, + "peerDependencies": { + "react": "^16.13.1", + "react-redux": "^7.2.0" + } +} diff --git a/packages/access/src/CheckAccess.jsx b/packages/access/src/CheckAccess.jsx new file mode 100644 index 0000000..7a532f3 --- /dev/null +++ b/packages/access/src/CheckAccess.jsx @@ -0,0 +1,46 @@ +import { connect } from 'react-redux' +import { createContext } from 'react' +import PropTypes from 'prop-types' + +const F_PUBLIC = 2 ** 0 +export const CheckAccessContext = createContext() + +CheckAccessProvider.propTypes = { + level: PropTypes.number.isRequired, + children: PropTypes.node.isRequired, +} + +function CheckAccessProvider({ children, level }) { + return ( + + {children} + + ) +} + +export default connect( + (state, props) => ({ + level: props.userLevelSelector ? props.userLevelSelector(state) : F_PUBLIC, + }) +)(CheckAccessProvider) + +CheckAccess.propTypes = { + access: PropTypes.number, + fallback: PropTypes.node, + children: PropTypes.node.isRequired, +} + +CheckAccess.defaultProps = { + access: F_PUBLIC, + fallback: null, +} + +export function CheckAccess({ access = F_PUBLIC, children, fallback }) { + return ( + + {level => { + return level & access ? children : fallback + }} + + ) +} diff --git a/packages/access/yarn.lock b/packages/access/yarn.lock new file mode 100644 index 0000000..9f5df32 --- /dev/null +++ b/packages/access/yarn.lock @@ -0,0 +1,73 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@babel/runtime@^7.5.5": + version "7.10.2" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.10.2.tgz#d103f21f2602497d38348a32e008637d506db839" + integrity sha512-6sF3uQw2ivImfVIl62RZ7MXhO2tap69WeWK57vAaimT6AZbE4FbqjdEJIN1UqoD6wI6B+1n9UiagafH1sxjOtg== + dependencies: + regenerator-runtime "^0.13.4" + +hoist-non-react-statics@^3.3.0: + version "3.3.2" + resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" + integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== + dependencies: + react-is "^16.7.0" + +"js-tokens@^3.0.0 || ^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +loose-envify@^1.1.0, loose-envify@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== + dependencies: + js-tokens "^3.0.0 || ^4.0.0" + +object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= + +prop-types@^15.6.2, prop-types@^15.7.2: + version "15.7.2" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" + integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ== + dependencies: + loose-envify "^1.4.0" + object-assign "^4.1.1" + react-is "^16.8.1" + +react-is@^16.7.0, react-is@^16.8.1, react-is@^16.9.0: + version "16.13.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" + integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== + +react-redux@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.2.0.tgz#f970f62192b3981642fec46fd0db18a074fe879d" + integrity sha512-EvCAZYGfOLqwV7gh849xy9/pt55rJXPwmYvI4lilPM5rUT/1NxuuN59ipdBksRVSvz0KInbPnp4IfoXJXCqiDA== + dependencies: + "@babel/runtime" "^7.5.5" + hoist-non-react-statics "^3.3.0" + loose-envify "^1.4.0" + prop-types "^15.7.2" + react-is "^16.9.0" + +react@^16.13.1: + version "16.13.1" + resolved "https://registry.yarnpkg.com/react/-/react-16.13.1.tgz#2e818822f1a9743122c063d6410d85c1e3afe48e" + integrity sha512-YMZQQq32xHLX0bz5Mnibv1/LHb3Sqzngu7xstSM+vrkE5Kzr9xE0yMByK5kMoTK30YVJE61WfbxIFFvfeDKT1w== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + prop-types "^15.6.2" + +regenerator-runtime@^0.13.4: + version "0.13.5" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz#d878a1d094b4306d10b9096484b33ebd55e26697" + integrity sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==