├── .gitignore
├── CONTRIBUTING.md
├── LICENSE.md
├── README.md
├── docs
├── assets
│ └── gatema.jpeg
└── out
│ ├── AllowOperations.html
│ ├── AllowOperations.js.html
│ ├── DisallowOperations.html
│ ├── DisallowOperations.js.html
│ ├── GateMan.html
│ ├── GateMan.js.html
│ ├── HasRolesAndClaims.html
│ ├── HasRolesAndClaims.js.html
│ ├── RoleOperations.html
│ ├── RoleOperations.js.html
│ ├── index.html
│ ├── scripts
│ ├── app.min.js
│ └── linenumber.js
│ └── styles
│ ├── app.min.css
│ ├── iframe.css
│ ├── prettify-jsdoc.css
│ ├── prettify-tomorrow.css
│ └── reset.css
├── index.js
├── package-lock.json
├── package.json
└── src
├── AllowOperations.js
├── DisallowOperations.js
├── GateMan.js
├── HasRolesAndClaims.js
├── Models
├── Claim.js
├── Role.js
├── RoleClaim.js
├── UserClaim.js
└── UserRole.js
├── RoleOperations.js
└── Tests
├── Gateman.spec.js
└── HasRolesAndAbilities.spec.js
/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | When contributing to this repository, please first discuss the change you wish to make via issue,
4 | email, or any other method with the owners of this repository before making a change.
5 |
6 | Please note we have a code of conduct, please follow it in all your interactions with the project.
7 |
8 | ## Pull Request Process
9 |
10 | 1. Ensure any install or build dependencies are removed before the end of the layer when doing a
11 | build.
12 | 2. Update the README.md with details of changes to the interface, this includes new environment
13 | variables, exposed ports, useful file locations and container parameters.
14 | 3. Increase the version numbers in any examples files and the README.md to the new version that this
15 | Pull Request would represent. The versioning scheme we use is [SemVer](http://semver.org/).
16 | 4. You may merge the Pull Request in once you have the sign-off of two other developers, or if you
17 | do not have permission to do that, you may request the second reviewer to merge it for you.
18 |
19 | ## Code of Conduct
20 |
21 | ### Our Pledge
22 |
23 | In the interest of fostering an open and welcoming environment, we as
24 | contributors and maintainers pledge to making participation in our project and
25 | our community a harassment-free experience for everyone, regardless of age, body
26 | size, disability, ethnicity, gender identity and expression, level of experience,
27 | nationality, personal appearance, race, religion, or sexual identity and
28 | orientation.
29 |
30 | ### Our Standards
31 |
32 | Examples of behavior that contributes to creating a positive environment
33 | include:
34 |
35 | * Using welcoming and inclusive language
36 | * Being respectful of differing viewpoints and experiences
37 | * Gracefully accepting constructive criticism
38 | * Focusing on what is best for the community
39 | * Showing empathy towards other community members
40 |
41 | Examples of unacceptable behavior by participants include:
42 |
43 | * The use of sexualized language or imagery and unwelcome sexual attention or
44 | advances
45 | * Trolling, insulting/derogatory comments, and personal or political attacks
46 | * Public or private harassment
47 | * Publishing others' private information, such as a physical or electronic
48 | address, without explicit permission
49 | * Other conduct which could reasonably be considered inappropriate in a
50 | professional setting
51 |
52 | ### Our Responsibilities
53 |
54 | Project maintainers are responsible for clarifying the standards of acceptable
55 | behavior and are expected to take appropriate and fair corrective action in
56 | response to any instances of unacceptable behavior.
57 |
58 | Project maintainers have the right and responsibility to remove, edit, or
59 | reject comments, commits, code, wiki edits, issues, and other contributions
60 | that are not aligned to this Code of Conduct, or to ban temporarily or
61 | permanently any contributor for other behaviors that they deem inappropriate,
62 | threatening, offensive, or harmful.
63 |
64 | ### Scope
65 |
66 | This Code of Conduct applies both within project spaces and in public spaces
67 | when an individual is representing the project or its community. Examples of
68 | representing a project or community include using an official project e-mail
69 | address, posting via an official social media account, or acting as an appointed
70 | representative at an online or offline event. Representation of a project may be
71 | further defined and clarified by project maintainers.
72 |
73 | ### Enforcement
74 |
75 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
76 | reported by contacting the project team at ibesoft11@gmail.com. All
77 | complaints will be reviewed and investigated and will result in a response that
78 | is deemed necessary and appropriate to the circumstances. The project team is
79 | obligated to maintain confidentiality with regard to the reporter of an incident.
80 | Further details of specific enforcement policies may be posted separately.
81 |
82 | Project maintainers who do not follow or enforce the Code of Conduct in good
83 | faith may face temporary or permanent repercussions as determined by other
84 | members of the project's leadership.
85 |
86 | ### Attribution
87 |
88 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
89 | available at [http://contributor-covenant.org/version/1/4][version]
90 |
91 | [homepage]: http://contributor-covenant.org
92 | [version]: http://contributor-covenant.org/version/1/4/
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | Copyright (c) 2019 Gatemanjs
2 |
3 | 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:
4 |
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 |
7 | 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.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # Gateman.js
4 |
5 | Gatemanjs is an authorization system designed to manage roles and claims in node applications that use mongodb for data storage. It works together with mongoose to provide a fluent approach to managing roles and claims.
6 |
7 | ## Installation
8 |
9 | You can install gateman using npm package manager.
10 |
11 | ```
12 | npm install gatemanjs
13 | ```
14 |
15 | ## Usage
16 |
17 | Before using gateman in your node application, you'll have to import the gateman package and setup the gateman class by passing in a valid mongoose connection object
18 |
19 | ```
20 | var mongoose = require('mongoose');
21 | var gateman = require("gatemanjs").GateMan(mongoose);
22 | ```
23 |
24 | ### Creating roles & claims
25 | You have to create a role before using it in your application, Gateman provides an easy way of doing that.
26 |
27 | ```
28 | //Syntax
29 | gateman.createRole(roleName);
30 |
31 | //Example
32 | let role = await gateman.createRole("rolename");
33 | ```
34 | Creating claims is similar to creating roles
35 |
36 | ```
37 | //Syntax
38 | gateman.createClaim(claimName);
39 |
40 | //Example
41 | let role = await gateman.createRole("claimname");
42 | ```
43 |
44 | ##### Note: To get a collection of existing roles, you can use
45 | ```
46 | //Syntax
47 | gateman.getRoles(callback);
48 |
49 | //Example
50 | let roles = await gateman.getRoles();
51 | ```
52 |
53 | ### Allowing members of a role to perform a claim
54 | Adding claims to roles is made extremely easy. You do not have to create a claim in advance. Simply pass the name of the claim, and Gateman will create it if it doesn't exist.
55 |
56 | ```
57 | gateman.allow('role').to('claim'); //for an existing role
58 | ```
59 | You can also assign a claim to a role immediately after creating it
60 |
61 | ```
62 | let role = await gateman.createRole("admin");
63 | await gateman.allow("admin").to("delete");
64 |
65 | //this provides every member of the admin role the claim to delete
66 | ```
67 |
68 | ### Disallowing members of a role from performing a claim
69 | Retracting claims from a role is very easy, you just need the rolename and claimname
70 |
71 | ```
72 | await gateman.disallow('role').from('claim');
73 |
74 | //Gateman does nothing if the role doesn't possess the claim
75 | ```
76 |
77 | ### Checking for Role claims
78 | Checking if a Claim has been assigned to a Role can be done this way
79 |
80 | ```
81 | let result = await gateman.role('rolename').can('claimname');
82 | //result is true if the claim has been assigned, else it will be false
83 |
84 | ```
85 |
86 | ### Using gateman with user models
87 |
88 | It is important to set up your User model to extend the HasRolesAndClaims class from the gateman package.
89 |
90 | ```
91 | const mongoose = require('mongoose');
92 | const hasRolesAndClaims = require('gatemanjs').hasRolesAndClaims(mongoose);
93 |
94 | var UserSchema = mongoose.Schema({
95 | name: String,
96 | email: String
97 | });
98 |
99 | UserSchema.loadClass(hasRolesAndClaims);
100 | module.exports = mongoose.model('User', UserSchema)
101 | ```
102 |
103 | After setting up your user model, you can call gateman methods on your mongoose user model.
104 |
105 | ### Allowing users to perform a claim
106 | ```
107 | //Example
108 |
109 | let user = await UserModel.findOne({name: "chioma"});
110 | await user.allow("claim");
111 |
112 | /*
113 | The Gateman hasRolesAndClaims class is loaded into a valid mongoose model which means that the methods are only accessible to valid user objects.
114 | */
115 |
116 | //Disallowing a user from performing a claim
117 |
118 | let user = await UserModel.findOne({name: "chioma"});
119 | await user.disallow("claim");
120 |
121 | ```
122 |
123 | ### Assigning a role to a user
124 | Before assigning a role to a user, make sure it has been created.
125 | ```
126 | //Example
127 |
128 | let user = await UserModel.findOne({name: "chioma"});
129 | await user.assign("role");
130 |
131 | /*
132 | The Gateman hasRolesAndClaims class is loaded into a valid mongoose model which means that the methods are only accessible to valid user objects.
133 | */
134 |
135 | //Retracting a role from a user
136 |
137 | let user = await UserModel.findOne({name: "chioma"});
138 | await user.retract("role");
139 |
140 | ```
141 |
142 | ### Checking for User claims and Roles
143 | Gateman provides an easy way of verifying if a user belongs to a role or can perform a claim
144 |
145 | ```
146 | //To verify if a User belongs to a Role
147 |
148 | let user = await User.findOne({name: "chioma"});
149 | let userHasRole = await user.isA("role");
150 | if (userHasRole){
151 | //user belongs to role
152 | }
153 |
154 | //To verify if a User can perform a claim
155 |
156 | let user = await User.findOne({name: "chioma"});
157 | let userHasClaim = await user.can("claim");
158 | if (userHasClaim){
159 | //user can perform claim
160 | }
161 | ```
162 |
163 | ### Retrieving User Roles and Claims
164 | Gateman provides an easy way of retrieving a User's roles and/or claims
165 |
166 | ```
167 | //Returns a collection of Roles assigned to a User
168 |
169 | let user = await User.findOne({name: "chioma"});
170 | let roles = await user.getRolesForUser();
171 | console.log(roles);
172 |
173 | //Returns a collection of Claims a User can perform
174 |
175 | let user = await User.findOne({name: "chioma"}, (err, user)=>{
176 | let claims = await user.getClaimsForUser();
177 | console.log(claims);
178 | ```
179 |
180 | ## Documentation
181 | * [Usage](http://htmlpreview.github.com/?https://github.com/NwangwuOsitadinma/gateman/master/docs/out/index.html): Gateman methods
182 |
183 |
184 | ## Contributing
185 |
186 | Please read [CONTRIBUTING.md](CONTRIBUTING.md) for details on our code of conduct, and the process for submitting pull requests to us.
187 |
188 |
189 | ## Authors
190 |
191 | * **Ositadinma Nwangwu** - [NwangwuOsitadinma](https://github.com/NwangwuOsitadinma)
192 | * **Ibe Ogele** - [Ibesoft11](https://github.com/Ibesoft11)
193 |
194 | ## License
195 |
196 | This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details
197 |
198 | ## Acknowledgments
199 | * This project was inspired by Joseph Silber's [Bouncer](https://github.com/JosephSilber/bouncer)
200 | * Mongoose was used to build this
201 |
--------------------------------------------------------------------------------
/docs/assets/gatema.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NwangwuOsitadinma/gateman/75bb7dcc9a2d3edf5e8417a136086628656d6d70/docs/assets/gatema.jpeg
--------------------------------------------------------------------------------
/docs/out/AllowOperations.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
94 | Contains methods for managing application roles and claims
95 | Provide a valid mongoose connection object that will be used to store application credentials
96 |
let user = await UserModel.findOne({name: "chioma"});
332 | let userHasClaim = await user.can("claim");
333 | if (userHasClaim) {
334 | console.log("I have the claim"); }
let user = await UserModel.findOne({name: "chioma"});
395 | let noClaim = await user.cannot("claim");
396 | if (noClaim) {
397 | console.log("I do not have the claim"); }
423 | disallows a user from performing a particular claim
424 |
425 |
426 |
Parameters:
427 |
428 |
429 |
430 |
431 |
432 |
433 |
Name
434 |
435 |
Type
436 |
437 |
Description
438 |
439 |
440 |
441 |
442 |
443 |
444 |
445 |
claimName
446 |
447 |
448 |
449 |
450 | string
451 |
452 |
453 |
454 |
represents the claim you want to retract from a user
455 | #### Usage
456 | ```
457 | let user = await UserModel.findOne({name: "chioma"});
458 | await user.disallow("claim");
459 | ```
let user = await UserModel.findOne({name: "chioma"});
614 | let userHasRole = await user.isA("role");
615 | if (userHasRole) {
616 | console.log("User belongs to role"); }
let user = await UserModel.findOne({name: "chioma"});
686 | let userHasRole = await user.isAn("role");
687 | if (userHasRole) {
688 | console.log("User belongs to role"); }
let user = await UserModel.findOne({name: "chioma"});
758 | let result = await user.isNotA("role");
759 | if (result) {
760 | console.log("User does not belong to role"); }
781 | checks if a user is not a member of a role
782 |
783 |
784 |
Parameters:
785 |
786 |
787 |
788 |
789 |
790 |
Name
791 |
792 |
Type
793 |
794 |
Description
795 |
796 |
797 |
798 |
799 |
800 |
801 |
802 |
roleName
803 |
804 |
805 |
806 | string
807 |
808 |
809 |
810 |
represents the role name
811 | #### Usage
812 | ```
813 | let user = await User.findOne({name: "chioma"});
814 | let result = await user.isNotAn("role");
815 | if (result){
816 | //user does not belongs to role
817 | }
818 | ```
let user = await UserModel.findOne({name: "chioma"});
834 | let result = await user.isNotAn("role");
835 | if (result) {
836 | console.log("User does not belong to role"); }
72 | Gatemanjs is an authorization system designed to manage roles and claims in node applications that use mongodb for data storage. It works together with mongoose to provide a fluent approach to managing roles and claims.
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
--------------------------------------------------------------------------------
/docs/out/scripts/app.min.js:
--------------------------------------------------------------------------------
1 | "use strict";$().ready(function(){});var sidebarIsVisible=!1,toggleSidebar=function(t){var i=!(0a&&window.pageYOffsete-o?t.find(".title").css({left:0,position:"absolute",bottom:"0",top:"auto"}):t.find(".title").css({left:i,bottom:"auto",position:"fixed",top:0}):t.find(".title").css({left:0,position:"absolute"})}var i=$(".vertical-section");if(0 a{
6 | font-weight: 100;
7 | text-decoration: none;
8 | color: #BDC3CB;
9 | font-family: sans-serif;
10 | }
11 | .bd__button > a:hover {
12 | color: #798897;
13 | }
--------------------------------------------------------------------------------
/docs/out/styles/prettify-jsdoc.css:
--------------------------------------------------------------------------------
1 | /* JSDoc prettify.js theme */
2 |
3 | /* plain text */
4 | .pln {
5 | color: #000000;
6 | font-weight: normal;
7 | font-style: normal;
8 | }
9 |
10 | /* string content */
11 | .str {
12 | color: #006400;
13 | font-weight: normal;
14 | font-style: normal;
15 | }
16 |
17 | /* a keyword */
18 | .kwd {
19 | color: #000000;
20 | font-weight: bold;
21 | font-style: normal;
22 | }
23 |
24 | /* a comment */
25 | .com {
26 | font-weight: normal;
27 | font-style: italic;
28 | }
29 |
30 | /* a type name */
31 | .typ {
32 | color: #000000;
33 | font-weight: normal;
34 | font-style: normal;
35 | }
36 |
37 | /* a literal value */
38 | .lit {
39 | color: #006400;
40 | font-weight: normal;
41 | font-style: normal;
42 | }
43 |
44 | /* punctuation */
45 | .pun {
46 | color: #000000;
47 | font-weight: bold;
48 | font-style: normal;
49 | }
50 |
51 | /* lisp open bracket */
52 | .opn {
53 | color: #000000;
54 | font-weight: bold;
55 | font-style: normal;
56 | }
57 |
58 | /* lisp close bracket */
59 | .clo {
60 | color: #000000;
61 | font-weight: bold;
62 | font-style: normal;
63 | }
64 |
65 | /* a markup tag name */
66 | .tag {
67 | color: #006400;
68 | font-weight: normal;
69 | font-style: normal;
70 | }
71 |
72 | /* a markup attribute name */
73 | .atn {
74 | color: #006400;
75 | font-weight: normal;
76 | font-style: normal;
77 | }
78 |
79 | /* a markup attribute value */
80 | .atv {
81 | color: #006400;
82 | font-weight: normal;
83 | font-style: normal;
84 | }
85 |
86 | /* a declaration */
87 | .dec {
88 | color: #000000;
89 | font-weight: bold;
90 | font-style: normal;
91 | }
92 |
93 | /* a variable name */
94 | .var {
95 | color: #000000;
96 | font-weight: normal;
97 | font-style: normal;
98 | }
99 |
100 | /* a function name */
101 | .fun {
102 | color: #000000;
103 | font-weight: bold;
104 | font-style: normal;
105 | }
106 |
107 | /* Specify class=linenums on a pre to get line numbering */
108 | ol.linenums {
109 | margin-top: 0;
110 | margin-bottom: 0;
111 | }
112 |
--------------------------------------------------------------------------------
/docs/out/styles/prettify-tomorrow.css:
--------------------------------------------------------------------------------
1 | /* Tomorrow Theme */
2 | /* Original theme - https://github.com/chriskempson/tomorrow-theme */
3 | /* Pretty printing styles. Used with prettify.js. */
4 | /* SPAN elements with the classes below are added by prettyprint. */
5 | /* plain text */
6 | .pln {
7 | color: #4d4d4c; }
8 |
9 | @media screen {
10 | /* string content */
11 | .str {
12 | color: #718c00; }
13 |
14 | /* a keyword */
15 | .kwd {
16 | color: #8959a8; }
17 |
18 | /* a comment */
19 | .com {
20 | color: #8e908c; }
21 |
22 | /* a type name */
23 | .typ {
24 | color: #4271ae; }
25 |
26 | /* a literal value */
27 | .lit {
28 | color: #f5871f; }
29 |
30 | /* punctuation */
31 | .pun {
32 | color: #4d4d4c; }
33 |
34 | /* lisp open bracket */
35 | .opn {
36 | color: #4d4d4c; }
37 |
38 | /* lisp close bracket */
39 | .clo {
40 | color: #4d4d4c; }
41 |
42 | /* a markup tag name */
43 | .tag {
44 | color: #c82829; }
45 |
46 | /* a markup attribute name */
47 | .atn {
48 | color: #f5871f; }
49 |
50 | /* a markup attribute value */
51 | .atv {
52 | color: #3e999f; }
53 |
54 | /* a declaration */
55 | .dec {
56 | color: #f5871f; }
57 |
58 | /* a variable name */
59 | .var {
60 | color: #c82829; }
61 |
62 | /* a function name */
63 | .fun {
64 | color: #4271ae; } }
65 | /* Use higher contrast and text-weight for printable form. */
66 | @media print, projection {
67 | .str {
68 | color: #060; }
69 |
70 | .kwd {
71 | color: #006;
72 | font-weight: bold; }
73 |
74 | .com {
75 | color: #600;
76 | font-style: italic; }
77 |
78 | .typ {
79 | color: #404;
80 | font-weight: bold; }
81 |
82 | .lit {
83 | color: #044; }
84 |
85 | .pun, .opn, .clo {
86 | color: #440; }
87 |
88 | .tag {
89 | color: #006;
90 | font-weight: bold; }
91 |
92 | .atn {
93 | color: #404; }
94 |
95 | .atv {
96 | color: #060; } }
97 | /* Style */
98 | /*
99 | pre.prettyprint {
100 | background: white;
101 | font-family: Consolas, Monaco, 'Andale Mono', monospace;
102 | font-size: 12px;
103 | line-height: 1.5;
104 | border: 1px solid #ccc;
105 | padding: 10px; }
106 | */
107 |
108 | /* Specify class=linenums on a pre to get line numbering */
109 | ol.linenums {
110 | margin-top: 0;
111 | margin-bottom: 0; }
112 |
113 | /* IE indents via margin-left */
114 | li.L0,
115 | li.L1,
116 | li.L2,
117 | li.L3,
118 | li.L4,
119 | li.L5,
120 | li.L6,
121 | li.L7,
122 | li.L8,
123 | li.L9 {
124 | /* */ }
125 |
126 | /* Alternate shading for lines */
127 | li.L1,
128 | li.L3,
129 | li.L5,
130 | li.L7,
131 | li.L9 {
132 | /* */ }
133 |
--------------------------------------------------------------------------------
/docs/out/styles/reset.css:
--------------------------------------------------------------------------------
1 | /* reset css */
2 | html, body, div, span, applet, object, iframe,
3 | h1, h2, h3, h4, h5, h6, p, blockquote, pre,
4 | a, abbr, acronym, address, big, cite, code,
5 | del, dfn, em, img, ins, kbd, q, s, samp,
6 | small, strike, strong, sub, sup, tt, var,
7 | b, u, i, center,
8 | dl, dt, dd, ol, ul, li,
9 | fieldset, form, label, legend,
10 | table, caption, tbody, tfoot, thead, tr, th, td,
11 | article, aside, canvas, details, embed,
12 | figure, figcaption, footer, header, hgroup,
13 | menu, nav, output, ruby, section, summary,
14 | time, mark, audio, video {
15 | margin: 0;
16 | padding: 0;
17 | border: 0;
18 | font-size: 100%;
19 | font: inherit;
20 | vertical-align: baseline;
21 | }
22 | /* HTML5 display-role reset for older browsers */
23 | article, aside, details, figcaption, figure,
24 | footer, header, hgroup, menu, nav, section {
25 | display: block;
26 | }
27 | body {
28 | line-height: 1;
29 | }
30 | ol, ul {
31 | list-style: none;
32 | }
33 | blockquote, q {
34 | quotes: none;
35 | }
36 | blockquote:before, blockquote:after,
37 | q:before, q:after {
38 | content: '';
39 | content: none;
40 | }
41 | table {
42 | border-collapse: collapse;
43 | border-spacing: 0;
44 | }
45 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | var GateMan = require('./src/GateMan');
2 | var rolesAndClaims = require('./src/HasRolesAndClaims');
3 |
4 | /**
5 | * Creates a new Gateman instance for managing application roles and claims
6 | * * Provide a valid mongoose connection object that will be used to store application credentials
7 | * * `dbConnection` a mongoose object
8 | * #### Usage
9 | ```
10 | var mongoose = require('mongoose');
11 | var gateman = require("gatemanjs").GateMan(mongoose);
12 | ```
13 | */
14 | exports.GateMan = (dbConnection)=>{
15 | return new GateMan(dbConnection);
16 | }
17 |
18 | /**
19 | * Initializes roles and abilities, to be extended by a valid *User* model
20 | * * Once extended by a *User* model, each User object has access to the methods
21 | * * `dbConnection` a mongoose object
22 | *
23 | * #### Usage
24 | ```
25 | const mogoose = require('mongoose');
26 | const RolesClaims = require('gatemanjs').hasRolesAndClaims(mogoose);
27 |
28 | var UserSchema = mongoose.Schema({
29 | name: String,
30 | email: String
31 | });
32 |
33 | UserSchema.loadClass(RolesClaims);
34 | module.exports = mongoose.model('User',UserSchema)
35 | ```
36 | */
37 | exports.hasRolesAndClaims = (dbConnection)=>{
38 | new rolesAndClaims(dbConnection);
39 | return rolesAndClaims;
40 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "gatemanjs",
3 | "version": "1.1.0",
4 | "description": "Gateman is package for handling user authorization in node-mongo applications",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "find ./src/Tests -name '*.spec.js' | xargs mocha -R spec"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "git+https://github.com/NwangwuOsitadinma/gateman.git"
12 | },
13 | "keywords": [
14 | "authorization"
15 | ],
16 | "author": "Nwangwu Ositadinma",
17 | "license": "ISC",
18 | "bugs": {
19 | "url": "https://github.com/NwangwuOsitadinma/gateman/issues"
20 | },
21 | "homepage": "https://github.com/NwangwuOsitadinma/gateman#readme",
22 | "Dependencies": {
23 | "mongoose": "^5.2.12"
24 | },
25 | "devDependencies": {
26 | "better-docs": "^1.4.7",
27 | "chai": "^4.2.0",
28 | "chai-as-promised": "^7.1.1",
29 | "express": "^4.16.3",
30 | "mocha": "^6.1.4"
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/AllowOperations.js:
--------------------------------------------------------------------------------
1 | var role, claim, roleClaim;
2 |
3 | class AllowOperations {
4 |
5 | /**
6 | * Contains methods for managing application roles and claims
7 | * * Provide a valid mongoose connection object that will be used to store application credentials
8 | * @mongoose a mongoose connection object
9 | */
10 | constructor(rol, clm, roleClm){
11 | role = rol;
12 | claim = clm;
13 | roleClaim = roleClm;
14 | }
15 |
16 | /**
17 | * pass in the claim to be assigned to a role
18 | * @param claimName {String} pass a claim as a string if you called allow
19 | * #### Usage
20 | ```
21 | await gateman.allow("rolename").to("claim");
22 | ```
23 | */
24 | async to(claimName) {
25 | try {
26 | if (this.operation === 'allow') {
27 | //find the role, allow was meant to do this
28 | let dbRole = await role.findOne({ name: this.roler });
29 | if (dbRole) {
30 | //assign role here
31 | let c = await claim.where('name', claimName).limit(1).exec();
32 | if (c.length > 0) {
33 | let rlclm = await roleClaim.findOne({ role: dbRole._id, claim: c[0]._id });
34 | if (rlclm) {
35 | return "Claim has already been assigned to Role";
36 | } else {
37 | await roleClaim.create({ role: dbRole._id, claim: c[0]._id });
38 | return;
39 | }
40 | } else {
41 | claim.create({ name: claimName }, (err, claimE) => {
42 | if (err) throw new Error(err);
43 | roleClaim.create({ role: dbRole._id, claim: claimE._id }, function (err, roleClaim) {
44 | if (err) throw err;
45 | return;
46 | });
47 | });
48 | }
49 | } else {
50 | throw new Error("role not found");
51 | }
52 | }
53 | } catch (error) {
54 | throw error;
55 | }
56 | }
57 |
58 | }
59 | module.exports = AllowOperations;
--------------------------------------------------------------------------------
/src/DisallowOperations.js:
--------------------------------------------------------------------------------
1 | var role, claim, roleClaim;
2 |
3 | class DisallowOperations {
4 |
5 | /**
6 | * Contains methods for managing application roles and claims
7 | * * Provide a valid mongoose connection object that will be used to store application credentials
8 | * @mongoose a mongoose connection object
9 | */
10 | constructor(rol, clm, roleClm){
11 | role = rol;
12 | claim = clm;
13 | roleClaim = roleClm;
14 | }
15 |
16 |
17 | /**
18 | * pass in the claim to be retracted from a role
19 | * @param claimName {String} the claim to retract from a role
20 | * #### Usage
21 | ```
22 | await gateman.disallow('rolename').from('claim')
23 | //Gateman does nothing if the role doesn't possess the claim
24 | ```
25 | */
26 | async from(claimName) {
27 | try {
28 | if (this.operation === 'dissallow') {
29 | let dbRole = await role.findOne({ name: this.roler });
30 | if (dbRole) {
31 | let dbClaim = await claim.findOne({ name: claimName });
32 | await roleClaim.findOneAndDelete({ role: dbRole, claim: dbClaim });
33 | return;
34 | }
35 | }
36 | } catch (error) {
37 | throw error;
38 | }
39 | }
40 |
41 | }
42 | module.exports = DisallowOperations;
--------------------------------------------------------------------------------
/src/GateMan.js:
--------------------------------------------------------------------------------
1 | var role = require('./Models/Role');
2 | var claim = require('./Models/Claim');
3 | var roleClaim = require('./Models/RoleClaim');
4 | var allowOps = require('./AllowOperations');
5 | var disallowOps = require('./DisallowOperations');
6 | var roleOps = require('./RoleOperations');
7 |
8 | class GateMan {
9 |
10 | /**
11 | * Contains methods for managing application roles and claims
12 | * * Provide a valid mongoose connection object that will be used to store application credentials
13 | * @mongoose a mongoose connection object
14 | */
15 | constructor(mongoose){
16 | role = role(mongoose);
17 | claim = claim(mongoose);
18 | roleClaim = roleClaim(mongoose);
19 | }
20 |
21 | /**
22 | * creates a new gateman role and returns the role
23 | * @param roleName {String} A string that represents the name of the role to be created
24 | * #### Usage
25 | ```
26 | let role = await gateman.createRole("rolename");
27 | ```
28 | */
29 | async createRole(roleName) {
30 | try {
31 | if (roleName.trim() === "") throw new Error("role name cannot be empty");
32 | if (typeof(roleName) !== 'string') throw new Error("role name must be a string");
33 | let dbRole = await role.findOne({ name: roleName });
34 | if (dbRole) return dbRole;
35 | let newDbRole = await role.create({ name: roleName });
36 | return newDbRole;
37 | } catch (error) {
38 | throw error;
39 | }
40 | }
41 |
42 | /**
43 | * deletes an existing gateman role, does nothing if role does not exist
44 | * @param roleName {String} A string that represents the name of the role to be removed
45 | * #### Usage
46 | ```
47 | gateman.removeRole("rolename");
48 | ```
49 | */
50 | async removeRole(roleName){
51 | try {
52 | if (typeof(roleName) !== 'string') throw new Error('role name must be a string');
53 | return await role.findOneAndDelete({name: roleName});
54 | } catch (error) {
55 | throw error;
56 | }
57 | }
58 |
59 | /**
60 | * returns a gateman role specified by the name given
61 | * @param roleName {String} A string that represents the name of the role to be returned
62 | * #### Usage
63 | ```
64 | let role = await gateman.getRole("rolename");
65 | ```
66 | */
67 | async getRole(roleName){
68 | try {
69 | if (roleName.trim() === '') return {};
70 | if (typeof(roleName) !== 'string') throw new Error('role name must be a string');
71 | let rol = await role.findOne({name:roleName});
72 | return rol;
73 | } catch (error) {
74 | throw error;
75 | }
76 | }
77 |
78 | /**
79 | * Allows members of a role to perform a claim
80 | * @param rolename {String} the rolename
81 | * #### Usage
82 | ```
83 | await gateman.allow("rolename").to("claim");
84 | ```
85 | */
86 | allow(rolename){
87 | let linker = new allowOps(role, claim, roleClaim);
88 | if (rolename.trim() === '') throw new Error('role name must be provided');
89 | if (typeof(rolename) !== 'string') throw new Error('role name must be a string');
90 | linker.operation = 'allow';
91 | linker.roler = rolename;
92 | return linker;
93 | }
94 |
95 | /**
96 | * Dissallows a member of a role from performing a claim
97 | * @param rolename {String} the rolename
98 | * #### Usage
99 | ```
100 | await gateman.disallow('rolename').from('claim');
101 | //Gateman does nothing if the role doesn't possess the claim
102 | ```
103 | */
104 | disallow(rolename){
105 | let linker = new disallowOps(role, claim, roleClaim);
106 | if (rolename.trim() === '') throw new Error('role name must be provided');
107 | if (typeof(rolename) !== 'string') throw new Error('role name must be a string');
108 | linker.operation = 'dissallow';
109 | linker.roler = rolename;
110 | return linker;
111 | }
112 |
113 | /**
114 | * Returns existing roles in the system
115 | * #### Usage
116 | ```
117 | let roles = await gateman.getRoles();
118 | console.log(roles); //prints collection of all existing roles
119 | ```
120 | */
121 | async getRoles(){
122 | try {
123 | return role.find({});
124 | } catch (error) {
125 | throw error;
126 | }
127 | }
128 |
129 | /**
130 | * Creates a new claim
131 | * @param claimName {String} A string that represents the name of the claim
132 | * #### Usage
133 | ```
134 | await gateman.createClaim("delete");
135 | ```
136 | * @returns Promise
137 | */
138 | async createClaim(claimName) {
139 | try {
140 | if (claimName === "") return "claim name cannot be empty";
141 | let dbClaim = await claim.findOne({ name: claimName });
142 | if (dbClaim) {
143 | return dbClaim;
144 | } else {
145 | let newDbClaim = await claim.create({ name: claimName });
146 | return newDbClaim;
147 | }
148 | } catch (error) {
149 | throw error;
150 | }
151 | }
152 |
153 |
154 | /**
155 | * Deletes an existing claim
156 | * @param claimName {String} A string that represents the name of the claim to be deleted
157 | * #### Usage
158 | ```
159 | await gateman.removeClaim("claimname");
160 | ```
161 | */
162 | async removeClaim(claimName){
163 | return claim.findOneAndDelete({name: claimName});
164 | }
165 |
166 | /**
167 | * Returns all claims existing in the system
168 | * #### Usage
169 | ```
170 | let claims = await gateman.getClaims();
171 | //claims is a collection of existing claims
172 | ```
173 | */
174 | async getClaims(){
175 | return claim.find({});
176 | }
177 |
178 | /**
179 | * Returns one claim that matches the claimName given
180 | * @param claimName {String} - The name of the claim to find
181 | * #### Usage
182 | ```
183 | let claim = await gateman.getClaim();
184 | ```
185 | */
186 | async getClaim(claimName){
187 | return claim.findOne({name:claimName});
188 | }
189 |
190 | /**
191 | * Returns an array of claims a role can perform
192 | * @param roleName {String} represents the name of the role
193 | * #### Usage
194 | ```
195 | let roleClaims = await gateman.getRoleClaims("rolename");
196 | ```
197 | */
198 | async getRoleClaims(roleName){
199 | try {
200 | let result = [];
201 | let dbRole = await role.findOne({ name: roleName });
202 | if (dbRole) {
203 | let roleClaims = await roleClaim.find({ role: dbRole._id }).populate('role claim');
204 | if (roleClaims.length === 0) return result;
205 | for (let i=0; i 0) {
159 | for (let u of ur){
160 | let rc = await roleClaim.findOne({ role: u.role, claim: c._id });
161 | if (rc) {
162 | return true;
163 | } else {
164 | let uc = await userClaim.findOne({ user: u.user, claim: c._id });
165 | return uc ? true : false;
166 | }
167 | }
168 | } else {
169 | let uc = await userClaim.findOne({ user: this._id, claim: c._id });
170 | return uc ? true : false;
171 | }
172 | } else {
173 | throw new Error("Error, claim does not exist");
174 | }
175 | } catch (error) {
176 | throw error;
177 | }
178 | }
179 |
180 | /**
181 | * checks whether a user does not have the ability to perform a claim
182 | * @param claimName {string} represents the claim name
183 | * #### Usage
184 | ```
185 | let user = await User.findOne({name: "chioma"});
186 | let noClaim = await user.cannot("claim");
187 | if (noClaim){
188 | console.log('I do not have the claim');
189 | }
190 | ```
191 | */
192 | async cannot(claimName) {
193 | try {
194 | let c = await claim.findOne({ name: claimName });
195 | if (c) {
196 | let urs = await userRole.find({ user: this._id });
197 | if (urs.length > 0) {
198 | urs.forEach(async (ur) => {
199 | let rc = await roleClaim.findOne({ role: ur.role, claim: c._id });
200 | if (rc) {
201 | return false;
202 | } else {
203 | let uc = await userClaim.findOne({ user: ur.user, claim: c._id });
204 | return uc ? false : true;
205 | }
206 | });
207 | } else {
208 | let uc = await userClaim.findOne({ user: this._id, claim: c._id });
209 | return uc ? false : true;
210 | }
211 | } else {
212 | throw new Error("Error, user or claim does not exist");
213 | }
214 | } catch (error) {
215 | throw error;
216 | }
217 | }
218 |
219 | /**
220 | * checks if a user is a member of a role
221 | * @param roleName {string} represents the role name
222 | * #### Usage
223 | ```
224 | let user = await User.findOne({name: "chioma"});
225 | let userHasRole = await user.isA("role");
226 | if (userHasRole){
227 | //user belongs to role
228 | }
229 | ```
230 | */
231 | async isA(roleName) {
232 | try {
233 | let r = await role.findOne({ name: roleName });
234 | if (r) {
235 | let ur = await userRole.findOne({ user: this._id, role: r._id });
236 | return ur ? true : false;
237 | } else {
238 | throw new Error("Error, role does not exist");
239 | }
240 | } catch (error) {
241 | throw error;
242 | }
243 | }
244 |
245 | /**
246 | * checks if a user is a member of a role
247 | * @param roleName {string} representing the role name
248 | * #### Usage
249 | ```
250 | let user = await User.findOne({name: "chioma"});
251 | let userHasRole = await user.isAn("role");
252 | if (userHasRole){
253 | //user belongs to role
254 | }
255 | ```
256 | */
257 | async isAn(roleName) {
258 | return await this.isA(roleName);
259 | }
260 |
261 | /**
262 | * checks if a user is not a member of a role
263 | * @param roleName {string} represents the role name
264 | * #### Usage
265 | ```
266 | let user = await User.findOne({name: "chioma"});
267 | let result = await user.isNotA("role");
268 | if (result){
269 | //user does not belongs to role
270 | }
271 | ```
272 | */
273 | async isNotA(roleName) {
274 | try {
275 | let r = await role.findOne({ name: roleName });
276 | if (r) {
277 | let ur = await userRole.findOne({ user: this._id, role: r._id });
278 | return ur ? false : true;
279 | } else {
280 | throw new Error("Error, user or role does not exist");
281 | }
282 | } catch (error) {
283 | throw error;
284 | }
285 | }
286 |
287 | /**
288 | * checks if a user is not a member of a role
289 | * @param roleName {string} represents the role name
290 | * #### Usage
291 | ```
292 | let user = await User.findOne({name: "chioma"});
293 | let result = await user.isNotAn("role");
294 | if (result){
295 | //user does not belongs to role
296 | }
297 | ```
298 | */
299 | async isNotAn(roleName) {
300 | return await this.isNotA(roleName);
301 | }
302 |
303 | /**
304 | * Returns a collection of Roles assigned to a User
305 | * #### Usage
306 | ```
307 | let user = await User.findOne({name: "chioma"}, (err, user)=>{
308 | let roles = await user.getRolesForUser();
309 | console.log(roles);
310 | ```
311 | */
312 | async getRolesForUser() {
313 | try {
314 | var result = [];
315 | let roles = await userRole.find({ user: this._id });
316 | if (roles.length < 1) {
317 | return result;
318 | } else {
319 | for (var item of roles) {
320 | let roler = await role.find({_id: item.role});
321 | if (roler.length > 0){
322 | result.push(roler[0].name)
323 | } else {
324 | return result;
325 | }
326 | if (roles[roles.length - 1] == item) {
327 | //return only when you've added every role
328 | //Node is non-blocking, so the engine will return an empty array if resolve(code) is place outside the for-loop
329 | return result;
330 | }
331 | }
332 | }
333 | } catch (error) {
334 | throw error;
335 | }
336 | }
337 |
338 | /**
339 | * Returns a collection of Claims a User can perform
340 | * #### Usage
341 | ```
342 | let user = await User.findOne({name: "chioma"});
343 | let claims = await user.getClaimsForUser();
344 | console.log(claims);
345 | ```
346 | */
347 | async getClaimsForUser() {
348 | try {
349 | var result = [];
350 | let userRoles = await userRole.find({ user: this._id });
351 | if (userRoles.length >= 1) {
352 | for (let userRole of userRoles) {
353 | let roleClaims = await roleClaim.find({ role: userRole.role });
354 | if (roleClaims.length >= 1) {
355 | for (var item of roleClaims) {
356 | let roleClm = await claim.findOne({_id: item.claim});
357 | result.push(roleClm.name);
358 | if (roleClaims[roleClaims.length - 1] == item) {
359 | let userClaims = await userClaim.find({ user: this._id });
360 | if (userClaims.length >= 1) {
361 | for (var item of userClaims) {
362 | let roleClm = await claim.findOne({_id: item.claim});
363 | result.push(roleClm.name);
364 | if (userClaims[userClaims.length - 1] == item) {
365 | return result.filter(this.onlyUnique);
366 | }
367 | }
368 | } else {
369 | return result.filter(this.onlyUnique);
370 | }
371 | }
372 | }
373 | } else {
374 | let userClaims = await userClaim.find({ user: this._id });
375 | if (userClaims.length >= 1) {
376 | for (var item of userClaims) {
377 | let roleClm = await claim.findOne({_id: item.claim});
378 | result.push(roleClm.name);
379 | if (userClaims[userClaims.length - 1] == item) {
380 | return result.filter(this.onlyUnique);
381 | }
382 | }
383 | }
384 | }
385 | }
386 | } else {
387 | let userClaims = await userClaim.find({ user: this._id });
388 | if (userClaims.length >= 1) {
389 | for (var item of userClaims) {
390 | let roleClm = await claim.findOne({_id: item.claim});
391 | result.push(roleClm.name);
392 | if (userClaims[userClaims.length - 1] == item) {
393 | return result.filter(this.onlyUnique);
394 | }
395 | }
396 | } else {
397 | return result.filter(this.onlyUnique);
398 | }
399 | }
400 | } catch (error) {
401 | throw error;
402 | }
403 |
404 | }
405 |
406 | onlyUnique(value, index, self) {
407 | return self.indexOf(value) === index;
408 | }
409 | }
410 |
411 | module.exports = HasRolesAndClaims;
--------------------------------------------------------------------------------
/src/Models/Claim.js:
--------------------------------------------------------------------------------
1 | module.exports = (mongoose)=>{
2 | var mn = mongoose.modelNames();
3 | var ClaimSchema = mongoose.Schema({
4 | name: {
5 | type: String,
6 | required: [true,'Sorry, the claim is required'],
7 | unique: true
8 | }
9 | });
10 | if (mn.includes("GatemanClaim")){
11 | return mongoose.model('GatemanClaim');
12 | } else {
13 | return mongoose.model('GatemanClaim',ClaimSchema);
14 | }
15 |
16 | }
--------------------------------------------------------------------------------
/src/Models/Role.js:
--------------------------------------------------------------------------------
1 | module.exports = (mongoose)=>{
2 | var mn = mongoose.modelNames();
3 | var RoleSchema = mongoose.Schema({
4 | name: {
5 | type: String,
6 | required: [true,'Sorry, the role name is required'],
7 | unique: true
8 | }
9 | });
10 | if (mn.includes("GatemanRole")){
11 | return mongoose.model("GatemanRole");
12 | } else {
13 | return mongoose.model("GatemanRole", RoleSchema);
14 | }
15 | }
--------------------------------------------------------------------------------
/src/Models/RoleClaim.js:
--------------------------------------------------------------------------------
1 | module.exports = (mongoose)=>{
2 | var mn = mongoose.modelNames();
3 | var RoleClaimSchema = mongoose.Schema({
4 | role: {
5 | type: mongoose.Schema.Types.ObjectId,
6 | required: [true,'Sorry, the role is required'],
7 | ref: 'GatemanRole'
8 | },
9 | claim: {
10 | type: mongoose.Schema.Types.ObjectId,
11 | required: [true,'Sorry! the claim is required'],
12 | ref: 'GatemanClaim'
13 | }
14 | });
15 | if (mn.includes("GatemanRoleClaim")){
16 | return mongoose.model('GatemanRoleClaim');
17 | } else {
18 | return mongoose.model('GatemanRoleClaim',RoleClaimSchema);
19 | }
20 |
21 | }
--------------------------------------------------------------------------------
/src/Models/UserClaim.js:
--------------------------------------------------------------------------------
1 | module.exports = (mongoose)=>{
2 | var mn = mongoose.modelNames();
3 | var UserClaimSchema = mongoose.Schema({
4 | user: {
5 | type: mongoose.Schema.Types.ObjectId,
6 | required: [true,'Sorry, the user is required']
7 | },
8 | claim: {
9 | type: mongoose.Schema.Types.ObjectId,
10 | required: [true,'Sorry, the claim is required'],
11 | ref: 'GatemanClaim'
12 | }
13 | });
14 | if (mn.includes("GatemanUserClaim")){
15 | return mongoose.model('GatemanUserClaim');
16 | } else {
17 | return mongoose.model('GatemanUserClaim',UserClaimSchema);
18 | }
19 |
20 | }
--------------------------------------------------------------------------------
/src/Models/UserRole.js:
--------------------------------------------------------------------------------
1 | module.exports = (mongoose)=>{
2 | var mn = mongoose.modelNames();
3 | var UserRoleSchema = mongoose.Schema({
4 | user: {
5 | type: mongoose.Schema.Types.ObjectId,
6 | required: [true,'Sorry, the user is required']
7 | },
8 | role: {
9 | type: mongoose.Schema.Types.ObjectId,
10 | required: [true,'Sorry, the role is required'],
11 | ref: 'GatemanRole'
12 | }
13 | });
14 | if (mn.includes("GatemanUserRole")){
15 | return mongoose.model('GatemanUserRole');
16 | } else {
17 | return mongoose.model('GatemanUserRole',UserRoleSchema);
18 | }
19 | }
--------------------------------------------------------------------------------
/src/RoleOperations.js:
--------------------------------------------------------------------------------
1 | var role, claim, roleClaim;
2 |
3 | class RoleOperations {
4 |
5 | /**
6 | * Contains methods for managing application roles and claims
7 | * * Provide a valid mongoose connection object that will be used to store application credentials
8 | * @mongoose a mongoose connection object
9 | */
10 | constructor(rol, clm, roleClm){
11 | role = rol;
12 | claim = clm;
13 | roleClaim = roleClm;
14 | }
15 |
16 | /**
17 | * Checks if a Role can perform a Claim, must be used after `gateman.role()`
18 | * @param claimName {String} A string that represents the name of the Claim
19 | * #### Usage
20 | ```
21 | let result = await gateman.role('rolename').can('claimname');
22 | //should be true if role has the claim
23 | ```
24 | */
25 | async can(claimName) {
26 | //find claim, role, then check if it has the claimName
27 | try {
28 | let dbClaim = await claim.findOne({ name: claimName });
29 | if (dbClaim) {
30 | let dbRole = await role.findOne({ name: this.roleName });
31 | if (dbRole) {
32 | let roleClm = await roleClaim.findOne({ role: dbRole._id, claim: dbClaim._id });
33 | return roleClm ? true : false;
34 | } else {
35 | return "role does not exist";
36 | }
37 | } else {
38 | throw new Error("claim does not exist");
39 | }
40 | } catch (error) {
41 | throw error;
42 | }
43 | }
44 |
45 | /**
46 | * Checks if a Role cannot perform a Claim, must be used after `gateman.role()`
47 | * @param claimName {String} A string that represents the name of the Claim
48 | * #### Usage
49 | ```
50 | let result = await gateman.role('rolename').cannnot('claimname');
51 | //result is false if the role has the claim
52 | ```
53 | */
54 | async cannot(claimName){
55 | //find claim, role, then check if it has the claimName
56 | try {
57 | let dbClaim = await claim.findOne({ name: claimName });
58 | if (dbClaim) {
59 | let dbRole = await role.findOne({ name: this.roleName });
60 | if (dbRole) {
61 | let roleClm = await roleClaim.findOne({ role: dbRole._id, claim: dbClaim._id });
62 | return roleClm ? false : true;
63 | } else {
64 | return "role does not exist";
65 | }
66 | } else {
67 | throw new Error("claim does not exist");
68 | }
69 | } catch (error) {
70 | throw error;
71 | }
72 | }
73 |
74 | }
75 | module.exports = RoleOperations;
--------------------------------------------------------------------------------
/src/Tests/Gateman.spec.js:
--------------------------------------------------------------------------------
1 | var chai = require('chai');
2 | var should = chai.should();
3 | var expect = chai.expect;
4 | var mongoose = require('mongoose');
5 | mongoose.set('useCreateIndex', true);
6 | mongoose.Promise = global.Promise;
7 | var gateman = require('../GateMan');
8 | var GateMan = new gateman(mongoose);
9 |
10 |
11 | describe('Gateman Claims', function(){
12 | before('ensuring the db is fresh',function(done){
13 | mongoose.connect('mongodb://localhost:27017/GateManTest',{useNewUrlParser: true});
14 | done();
15 | });
16 | after('ensuring everything is cleaned up',function(done){
17 | mongoose.disconnect();
18 | done();
19 | });
20 |
21 | describe('createClaim', function () {
22 | it('should return a claim object if created successfully', async() => {
23 | var c = 'turn-water-into-wine';
24 | let claim = await GateMan.createClaim(c);
25 | should.equal(claim.name, c);
26 | }).timeout(10000);
27 | });
28 |
29 | describe('removeClaim', function(){
30 | it('should not return anything if it actually deleted', async()=>{
31 | var c = 'turn-water-into-wine';
32 | let claim = await GateMan.createClaim(c);
33 | await GateMan.removeClaim(claim.name);
34 | let clm = await GateMan.getClaim(claim.name);
35 | should.equal(clm,null);
36 | }).timeout(10000);
37 | });
38 |
39 | describe('getClaim',function(){
40 | it('should return one valid claim', async()=>{
41 | var c = 'turn-water-into-wine';
42 | let claim = await GateMan.createClaim(c);
43 | await GateMan.getClaim(c);
44 | should.equal(claim.name,c);
45 | }).timeout(10000);
46 | });
47 |
48 | describe('getClaims',function(){
49 | it('should return a collection of claims with at least one member', async()=>{
50 | var c = 'turn-water-into-wine';
51 | var c2 = 'claim2';
52 | await GateMan.createClaim(c);
53 | await GateMan.createClaim(c2);
54 | let claims = await GateMan.getClaims();
55 | claims.should.not.be.empty;
56 | }).timeout(10000);
57 | });
58 |
59 | });
60 |
61 | describe('Gateman Roles',function(){
62 | before('ensuring db is fresh',(done)=>{
63 | mongoose.connect('mongodb://localhost:27017/GateManTest',{useNewUrlParser: true});
64 | done();
65 | });
66 | after('ensuring everything is cleaned up',function(done){
67 | mongoose.disconnect();
68 | done();
69 | });
70 | describe('createRole', function(){
71 | it ('should return a role object if created successfully', async()=>{
72 | var r = 'admin';
73 | let role = await GateMan.createRole(r);
74 | should.equal(role.name,r);
75 | }).timeout(10000);
76 | });
77 | describe('removeRole',function(){
78 | it('should not return anything if it actually deleted',async ()=>{
79 | var r = 'roler';
80 | let role = await GateMan.createRole(r);
81 | await GateMan.removeRole(role.name);
82 | let rol = await GateMan.getRole(role.name);
83 | should.equal(rol, null);
84 | }).timeout(10000);
85 | });
86 |
87 | describe('getRole',function(){
88 | it('should return one valid role', async()=>{
89 | var r = "admin";
90 | await GateMan.createRole(r)
91 | let role = await GateMan.getRole(r);
92 | should.equal(role.name,r);
93 | }).timeout(10000);
94 | });
95 |
96 | describe('getRoles',function(){
97 | it('should return a collection of at least one valid role', async()=>{
98 | var r = "admin";
99 | await GateMan.createRole(r);
100 | let roles = await GateMan.getRoles();
101 | roles.should.not.be.empty;
102 | }).timeout(10000)
103 | });
104 |
105 | describe('Roles with claims', function(){
106 | it('should return a role that has 2 claims', async()=>{
107 | await GateMan.createRole('admin');
108 | await GateMan.allow('admin').to('add');
109 | await GateMan.allow('admin').to('delete');
110 | let roleClaims = await GateMan.getRoleClaims('admin');
111 | expect(roleClaims).to.be.an('array');
112 | expect(roleClaims).to.have.members(['add','delete']);
113 | });
114 |
115 | it('should return a role that has no claims', async()=>{
116 | await GateMan.createRole('teacher');
117 | await GateMan.allow('teacher').to('add');
118 | await GateMan.disallow('teacher').from('add');
119 | let roleClaims = await GateMan.getRoleClaims('teacher');
120 | expect(roleClaims).to.be.an('array');
121 | expect(roleClaims).to.have.members([]);
122 | });
123 |
124 | it('should return true if role has claim', async ()=> {
125 | await GateMan.createRole('manager');
126 | await GateMan.createClaim('edit');
127 | await GateMan.allow('manager').to('edit');
128 | let hasClaim = await GateMan.role('manager').can('edit');
129 | expect(hasClaim).to.be.true;
130 | });
131 |
132 | it('should return false if role does not have claim', async ()=> {
133 | await GateMan.createRole('manager');
134 | let hasClaim = await GateMan.role('manager').can('add');
135 | expect(hasClaim).to.be.false;
136 | });
137 |
138 | it('should return false if role has claim', async ()=> {
139 | await GateMan.createRole('manager');
140 | await GateMan.createClaim('edit');
141 | await GateMan.allow('manager').to('edit');
142 | let hasClaim = await GateMan.role('manager').cannot('edit');
143 | expect(hasClaim).to.be.false;
144 | });
145 |
146 | it('should return true if role does not have claim', async ()=> {
147 | await GateMan.createRole('manager');
148 | let hasClaim = await GateMan.role('manager').cannot('delete');
149 | expect(hasClaim).to.be.true;
150 | });
151 |
152 | });
153 | });
--------------------------------------------------------------------------------
/src/Tests/HasRolesAndAbilities.spec.js:
--------------------------------------------------------------------------------
1 | var chai = require('chai');
2 | var should = chai.should();
3 | var expect = chai.expect;
4 | var mongoose = require('mongoose');
5 | mongoose.set('useCreateIndex', true);
6 | mongoose.Promise = global.Promise;
7 | // var gateman = require('../GateMan');
8 | // var GateManClsss = require('../../index').GateMan(mongoose);
9 | const hasRolesAndClaims = require('../../index').hasRolesAndClaims(mongoose);
10 |
11 | var UserSchema = new mongoose.Schema({
12 | name: String,
13 | email: String
14 | });
15 |
16 | UserSchema.loadClass(hasRolesAndClaims);
17 | var userModel = mongoose.model('User',UserSchema);
18 |
19 | describe('Roles and Abilities with Mongoose Objects',function(){
20 | before('ensuring db is fresh',async()=>{
21 | await mongoose.connect('mongodb://localhost:27017/GateManTest',{useNewUrlParser: true});
22 | await userModel.create({
23 | name: 'chioma',
24 | email: 'random@server.ng'
25 | });
26 | });
27 | after('ensuring everything is cleaned up',function(done){
28 | mongoose.disconnect();
29 | userModel.findOneAndDelete({
30 | name: 'chioma',
31 | email: 'random@server.ng'
32 | });
33 |
34 | done();
35 | });
36 |
37 | it('should allow user perform a claim', async ()=> {
38 | let user = await userModel.findOne({name: "chioma"});
39 | await user.allow('add');
40 | let hasClaim = await user.can('add');
41 | expect(hasClaim).to.be.true;
42 | });
43 |
44 | it('should check if user belongs to a role', async ()=> {
45 | let user = await userModel.findOne({name: "chioma"});
46 | let hasRole = await user.isAn('admin'); //role exists
47 | expect(hasRole).to.be.false;
48 | });
49 |
50 | it('should assign role to user', async ()=> {
51 | let user = await userModel.findOne({name: "chioma"});
52 | await user.assign('admin');
53 | let hasRole = await user.isAn('admin'); //role exists
54 | expect(hasRole).to.be.true;
55 | });
56 |
57 | it('should get user roles', async ()=> {
58 | let user = await userModel.findOne({name: "chioma"});
59 | let roles = await user.getRolesForUser();
60 | expect(roles).to.have.members(['admin']);
61 | });
62 |
63 | it('should get user claims', async ()=> {
64 | let user = await userModel.findOne({name: "chioma"});
65 | let claims = await user.getClaimsForUser();
66 | expect(claims).to.have.members(['add','delete']);
67 | });
68 |
69 | it('should retract role from user', async ()=> {
70 | let user = await userModel.findOne({name: "chioma"});
71 | await user.retract('admin');
72 | let hasRole = await user.isAn('admin'); //role exists
73 | expect(hasRole).to.be.false;
74 | });
75 |
76 | });
--------------------------------------------------------------------------------