diff --git a/LICENSE.md b/LICENSE.md
new file mode 100644
index 0000000..e9e74c7
--- /dev/null
+++ b/LICENSE.md
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2018 Nurzhan Saktaganov
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..6a66094
--- /dev/null
+++ b/README.md
@@ -0,0 +1,156 @@
+# React RBAC Guard #
+
+`react-rbac-guard` is a module allowing to manage visibility of particular components depending on user credentials (or current permissions set). Module uses approach that was inspired by "react-redux-connect" module.
+
+## Dependensies ##
+
+React RBAC requires either React [new context API](https://reactjs.org/docs/context.html) or React [legacy context API](https://reactjs.org/docs/legacy-context.html) support.
+Module tries to use new context API (React version >= 16.3) if available. Otherwise fallbacks to legacy context API. Legacy context API has problem with returning `false` from `shouldComponentUpdate` in intermediate components (see docs).
+
+## Installation ##
+
+```bash
+#TODO
+```
+
+## Integration in 5 easy steps ##
+
+1. Define your own class derived from `Requirement` class. It must implement `isSatisfied` method that takes credentials as parameter. The method must return `true` if requirement is satisfied by credentials and `false` otherwise
+```js
+import { Requirement } from "react-rbac-guard";
+
+class Need extends Requirement {
+ constructor(permission) {
+ super();
+ this.permission = permission;
+ }
+
+ isSatisfied(credentials) {
+ // assume credentilas is an object
+ return credentials[this.permission] ? true : false;
+ }
+}
+
+```
+
+2. Create requirements
+```js
+const NeedManagePost = new Need("CanManagePost");
+const NeedManageComment = new Need("CanManageComment");
+const NeedManageUser = new Need("CanManageUser");
+```
+
+3. Create guards
+```js
+import { guardFactory } from "react-rbac-guard";
+
+const PostManager = guardFactory(NeedManagePost);
+const CommentManager = guardFactory(NeedManageComment);
+const UserManager = guardFactory(NeedManageUser);
+```
+
+4. Provide credentials via `CredentialProvider` and use guards as components
+```jsx
+import { CredentialProvider } from "react-rbac-guard";
+
+class App extends Component {
+ render() {
+ const credentials = {}; // you have to provide it
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+ }
+}
+
+```
+
+5. Enjoy!
+
+## Capabilities ##
+
+1. You can use `all`, `any`, `not` functions to combine requirements.
+```jsx
+// Let's assume we have NeedAdmin, NeedManager, NeedUser, NeedGuest requirements.
+// You can produce new ones by combining them.
+import { any, not } from "react-rbac-gurad";
+
+const NeedAuthorized = not(NeedGuest);
+const NeedExtendedRights = any(NeedAdmin, NeedManager);
+
+```
+In other words, you can define arbitrary predicate (in terms of mathematical logic) based on your _requirements_.
+
+2. You can use Guard component directly (without guardFactory).
+```jsx
+
+
+
+```
+
+3. You can use `protect` to protect your components (it behaves like `connect` from `react-redux-connect` ).
+```jsx
+import { protect } from "react-rbac-guard";
+
+class CustomersList extends Component {
+ ...
+}
+
+// exports decorated component (like connect in "react-redux-connect")
+export default protect(NeedExtendedRights)(CustomersList);
+```
+or to protect external components
+```jsx
+import { Button } from "react-bootstrap";
+import { protect } from "react-rbac-guard";
+
+const SignUpButton = protect(NeedGuest)(Button);
+const SignOutButton = protect(NeedAuthrized)(Button);
+```
+
+4. It's also possible to make a decision inside credentials object
+```jsx
+class Credentials {
+ satisfies(requirement) {
+ return ...// return boolean depending on requirement
+ }
+}
+
+class MyRequirement extends Requirement {
+ ...
+ isSatisfied(credentials) {
+ return credentilas.satisfies(this);
+ }
+}
+
+const credentials = new Credentials(...);
+
+
+ ...
+
+
+```
+
+## Demo ##
+
+To see demos please visit [TODO].
+
+
+## License ##
+[MIT](./LICENSE.md)
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..258354e
--- /dev/null
+++ b/package.json
@@ -0,0 +1,25 @@
+{
+ "name": "react-rbac-guard",
+ "version": "0.0.1",
+ "description": "Module allowing to manage visibility of particular components depending on user credentials",
+ "main": "src/index.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/nurzhan-saktaganov/react-rbac-guard.git"
+ },
+ "keywords": [
+ "rbac",
+ "guard",
+ "protect",
+ "congitional render"
+ ],
+ "author": "Nurzhan Saktaganov ",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/nurzhan-saktaganov/react-rbac-guard/issues"
+ },
+ "homepage": "https://github.com/nurzhan-saktaganov/react-rbac-guard#readme"
+}
diff --git a/src/Guard.jsx b/src/Guard.jsx
new file mode 100644
index 0000000..1c0045a
--- /dev/null
+++ b/src/Guard.jsx
@@ -0,0 +1,26 @@
+import React, { Component } from "react";
+
+import createReactContext from "./createContext.jsx";
+import Requirement from "./Requirement";
+
+const { Provider, Consumer } = createReactContext();
+
+class Guard extends Component {
+ render() {
+ const { requirement } = this.props;
+
+ if (!(requirement instanceof Requirement)) {
+ throw new TypeError("requirement is expected to be Requirement instance");
+ }
+
+ return (
+
+ {credentials =>
+ requirement.isSatisfied(credentials) ? this.props.children : null
+ }
+
+ );
+ }
+}
+
+export { Guard, Provider };
diff --git a/src/Requirement.js b/src/Requirement.js
new file mode 100644
index 0000000..1f5854d
--- /dev/null
+++ b/src/Requirement.js
@@ -0,0 +1,14 @@
+class Requirement {
+ constructor() {
+ if (new.target === Requirement) {
+ throw new TypeError("Cannot construct Requirement instances directly");
+ }
+ this.isSatisfied = this.isSatisfied.bind(this);
+ }
+
+ isSatisfied(credentials) {
+ throw new Error("Must override isSatisfied");
+ }
+}
+
+export default Requirement;
diff --git a/src/RequirementAll.js b/src/RequirementAll.js
new file mode 100644
index 0000000..ec04af3
--- /dev/null
+++ b/src/RequirementAll.js
@@ -0,0 +1,9 @@
+import RequirementPredicate from "./RequirementPredicate";
+
+class RequirementAll extends RequirementPredicate {
+ isSatisfied(credentials) {
+ return this.requirements.every(r => r.isSatisfied(credentials));
+ }
+}
+
+export default RequirementAll;
diff --git a/src/RequirementAny.js b/src/RequirementAny.js
new file mode 100644
index 0000000..64b4571
--- /dev/null
+++ b/src/RequirementAny.js
@@ -0,0 +1,9 @@
+import RequirementPredicate from "./RequirementPredicate";
+
+class RequirementAny extends RequirementPredicate {
+ isSatisfied(credentials) {
+ return this.requirements.some(r => r.isSatisfied(credentials));
+ }
+}
+
+export default RequirementAny;
diff --git a/src/RequirementNot.js b/src/RequirementNot.js
new file mode 100644
index 0000000..5cc2abc
--- /dev/null
+++ b/src/RequirementNot.js
@@ -0,0 +1,14 @@
+import RequirementPredicate from "./RequirementPredicate";
+
+class RequirementNot extends RequirementPredicate {
+ constructor(requirement) {
+ super(...[requirement]);
+ this.requirement = requirement;
+ }
+
+ isSatisfied(credentials) {
+ return !this.requirement.isSatisfied(credentials);
+ }
+}
+
+export default RequirementNot;
diff --git a/src/RequirementPredicate.js b/src/RequirementPredicate.js
new file mode 100644
index 0000000..43d39b2
--- /dev/null
+++ b/src/RequirementPredicate.js
@@ -0,0 +1,26 @@
+import Requirement from "./Requirement";
+
+class RequirementPredicate extends Requirement {
+ constructor(...requirements) {
+ super();
+ if (new.target === RequirementPredicate) {
+ throw new TypeError(
+ "Cannot construct RequirementPredicate instances directly"
+ );
+ }
+
+ if (requirements.length === 0) {
+ throw new Error("No requirement has been provided");
+ }
+
+ if (requirements.some(r => !(r instanceof Requirement))) {
+ throw new Error(
+ "requirements are expected to be instances of 'Requirement'"
+ );
+ }
+
+ this.requirements = requirements;
+ }
+}
+
+export default RequirementPredicate;
diff --git a/src/createContext.jsx b/src/createContext.jsx
new file mode 100644
index 0000000..1949042
--- /dev/null
+++ b/src/createContext.jsx
@@ -0,0 +1,77 @@
+import React, { Component } from "react";
+
+import PropTypes from "prop-types";
+
+function generateToken(length) {
+ const symbolSet =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+ let token = "";
+
+ for (let i = 0; i < length; i++) {
+ const position = Math.floor(Math.random() * symbolSet.length);
+ token += symbolSet.charAt(position);
+ }
+
+ return token;
+}
+
+let reservedToken = {};
+function uniqueToken(length) {
+ let token;
+ do {
+ token = generateToken(length);
+ } while (reservedToken[token]);
+ reservedToken[token] = true;
+ return token;
+}
+
+// This is just polyfill to emulate new context API using legacy API.
+// NOTE: it does not support default value.
+function createContext(defaultValue) {
+ const TOKEN_LENGTH = 32;
+ const token = uniqueToken(TOKEN_LENGTH);
+ let contextTypes = {};
+ contextTypes[token] = PropTypes.any;
+
+ class Consumer extends Component {
+ render() {
+ const credentials = this.context[token];
+
+ const { children } = this.props;
+
+ if (!children) {
+ return null;
+ }
+
+ if (Array.isArray(children) || typeof children !== "function") {
+ throw new TypeError("Consumer expected exactly one function child");
+ }
+
+ return this.props.children(credentials);
+ }
+ }
+
+ Consumer.contextTypes = contextTypes;
+
+ class Provider extends Component {
+ getChildContext() {
+ let context = {};
+ context[token] = this.props.value;
+ return context;
+ }
+
+ render() {
+ return this.props.children;
+ }
+ }
+
+ Provider.childContextTypes = contextTypes;
+
+ return { Provider, Consumer };
+}
+
+export default function(defaultValue) {
+ // Try to use new context API, fallback to legacy API otherwise.
+ const func = React.createContext || createContext;
+ return func(defaultValue);
+}
diff --git a/src/guardFactory.jsx b/src/guardFactory.jsx
new file mode 100644
index 0000000..eec9c3d
--- /dev/null
+++ b/src/guardFactory.jsx
@@ -0,0 +1,18 @@
+import React, { Component } from "react";
+
+import Requirement from "./Requirement";
+import { Guard } from "./Guard";
+
+function guardFactory(requirement) {
+ if (!(requirement instanceof Requirement)) {
+ throw new TypeError("requirement is expected to be Requirement instance");
+ }
+
+ return class extends Component {
+ render() {
+ return {this.props.children};
+ }
+ };
+}
+
+export default guardFactory;
diff --git a/src/index.js b/src/index.js
new file mode 100644
index 0000000..d9e2509
--- /dev/null
+++ b/src/index.js
@@ -0,0 +1,31 @@
+import Requirement from "./Requirement";
+import RequirementAll from "./RequirementAll";
+import RequirementAny from "./RequirementAny";
+import RequirementNot from "./RequirementNot";
+
+import { Guard, Provider as CredentialProvider } from "./Guard";
+import guardFactory from "./guardFactory";
+import protect from "./protect";
+
+function all(...requirements) {
+ return new RequirementAll(...requirements);
+}
+
+function any(...requirements) {
+ return new RequirementAny(...requirements);
+}
+
+function not(requirement) {
+ return new RequirementNot(requirement);
+}
+
+export {
+ Requirement,
+ all,
+ any,
+ not,
+ CredentialProvider,
+ Guard,
+ guardFactory,
+ protect
+};
diff --git a/src/protect.jsx b/src/protect.jsx
new file mode 100644
index 0000000..ae5a2c7
--- /dev/null
+++ b/src/protect.jsx
@@ -0,0 +1,35 @@
+import React, { Component } from "react";
+
+import Requirement from "./Requirement";
+import { Guard } from "./Guard";
+
+function protect(requirement) {
+ if (!(requirement instanceof Requirement)) {
+ throw new TypeError("requirement is expected to be Requirement instance");
+ }
+
+ return function(ComponentToProtect) {
+ const isComponent =
+ ComponentToProtect &&
+ ComponentToProtect.prototype &&
+ ComponentToProtect.prototype instanceof Component;
+
+ if (!isComponent) {
+ throw new TypeError("expected a class derived from React.Component");
+ }
+
+ return class extends Component {
+ render() {
+ return (
+
+
+ {this.props.children}
+
+
+ );
+ }
+ };
+ };
+}
+
+export default protect;