├── .gitignore ├── AuthManager └── MongoritoScheme.js ├── LICENSE ├── README.md ├── Serializer └── MongoritoSerializer.js ├── package.json └── providers └── MongoritoProvider.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # nyc test coverage 18 | .nyc_output 19 | 20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 21 | .grunt 22 | 23 | # node-waf configuration 24 | .lock-wscript 25 | 26 | # Compiled binary addons (http://nodejs.org/api/addons.html) 27 | build/Release 28 | 29 | # Dependency directories 30 | node_modules 31 | jspm_packages 32 | 33 | # Optional npm cache directory 34 | .npm 35 | 36 | # Optional REPL history 37 | .node_repl_history 38 | -------------------------------------------------------------------------------- /AuthManager/MongoritoScheme.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const BaseScheme = require('adonis-auth/src/Schemes/BaseScheme') 4 | const NE = require('node-exceptions') 5 | 6 | class MongoritoScheme extends BaseScheme { 7 | 8 | /** 9 | * returns key to be used for saving session value. 10 | * @return {String} 11 | * 12 | * @public 13 | */ 14 | get sessionKey () { 15 | return 'mongodb-auth' 16 | } 17 | 18 | /** 19 | * tries to resolve the user for the current request or 20 | * returns null 21 | * 22 | * @return {Object} 23 | * 24 | * @private 25 | */ 26 | * _getRequestUser () { 27 | const userSession = yield this.request.session.get(this.sessionKey) 28 | if (!userSession) { 29 | return null 30 | } 31 | return yield this.serializer.findById(userSession, this.options) 32 | } 33 | 34 | /** 35 | * login a user using the user object, it blindly trusts the 36 | * input. 37 | * 38 | * @param {Object} user 39 | * @return {Boolean} 40 | * 41 | * @throws InvalidArgumentException when primary key value does not exists 42 | * on the input 43 | * 44 | * @public 45 | */ 46 | * login (user) { 47 | const primaryKey = this.serializer.primaryKey(this.options) 48 | const primaryValue = user.attributes[primaryKey] 49 | if (!primaryValue) { 50 | throw new NE.InvalidArgumentException(`Value for ${primaryKey} is null for given user.`) 51 | } 52 | yield this.request.session.put(this.sessionKey, primaryValue) 53 | this.user = user 54 | return true 55 | } 56 | 57 | /** 58 | * logout a user by removing session and setting 59 | * local instance to null 60 | * 61 | * @return {Boolean} 62 | * 63 | * @public 64 | */ 65 | * logout () { 66 | yield this.request.session.forget(this.sessionKey) 67 | this.user = null 68 | return true 69 | } 70 | 71 | /** 72 | * login a user using the userId, it does verify the user 73 | * using the serializer. 74 | * 75 | * @param {Number} userId 76 | * @return {Boolean} 77 | * 78 | * @public 79 | */ 80 | * loginViaId (userId) { 81 | const user = yield this.serializer.findById(userId, this.options) 82 | if (!user) { 83 | return false 84 | } 85 | return yield this.login(user) 86 | } 87 | 88 | /** 89 | * validates user credentials using validate method and login a 90 | * user if validate succeeds. 91 | * 92 | * @param {String} uid 93 | * @param {String} password 94 | * @return {Boolean} 95 | * 96 | * @see validate 97 | * @see login 98 | * 99 | * @public 100 | */ 101 | * attempt (uid, password) { 102 | const user = yield this.validate(uid, password, true) 103 | return yield this.login(user) 104 | } 105 | 106 | } 107 | 108 | module.exports = MongoritoScheme -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Allan Freitas 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # adonis-mongorito 2 | AdonisJS Addon Provider for Mongorito (mongodb package for nodejs) 3 | 4 | 5 | ## Installation 6 | 7 | ```js 8 | $ npm install adonis-mongorito --save 9 | ``` 10 | The command above will install the package mongorito too. 11 | 12 | You need to create a `config/mongo.js` file with the contents: 13 | 14 | ```js 15 | 'use strict' 16 | 17 | const Env = use('Env') 18 | 19 | module.exports = { 20 | host: Env.get('MONGO_HOST', 'localhost'), 21 | port: Env.get('MONGO_PORT', '27017'), 22 | user: Env.get('MONGO_USER', ''), 23 | pass: Env.get('MONGO_PASS', ''), 24 | db: Env.get('MONGO_DATABASE', 'adonisjs'), 25 | options: Env.get('MONGO_OPTIONS', '') 26 | } 27 | ``` 28 | *You are using a connection local without user and pass, leave it blank like on the example.* 29 | 30 | Add the entry `adonis-mongorito/providers/MongoritoProvider.js` to the providers array on `bootstrap/app.js` like this: 31 | 32 | ```js 33 | const providers = [ 34 | 'adonis-framework/providers/ConfigProvider', 35 | 'adonis-framework/providers/EnvProvider', 36 | //..OTHER DEFAULT PROVIDERS...// 37 | 'adonis-middleware/providers/AppMiddlewareProvider', 38 | 'adonis-auth/providers/AuthManagerProvider', 39 | 'adonis-mongorito/providers/MongoritoProvider' //add this line after install the package 40 | ] 41 | ``` 42 | 43 | The following its to add the Authenticator combination so you can perform Mongo db check in authentication. 44 | 45 | ``` 46 | // change here if you are using jwt, otherwise change it in you right authenticator 47 | jwt: { 48 | serializer: 'MongoritoSerializer', // this is new 49 | model: 'App/Model/User', 50 | scheme: 'MongoritoScheme', // and this too 51 | uid: 'username', 52 | password: 'password', 53 | secret: Config.get('app.appKey') 54 | } 55 | ``` 56 | 57 | Add the entry `MongoritoModel: 'Adonis/Addons/MongoritoModel'` on the `aliases` object on `bootstrap/app.js` file like this: 58 | 59 | ```js 60 | const aliases = { 61 | Command: 'Adonis/Src/Command', 62 | Config: 'Adonis/Src/Config', 63 | //..OTHER DEFAULT PROVIDERS...// 64 | View: 'Adonis/Src/View', 65 | MongoritoModel: 'Adonis/Addons/MongoritoModel' //this line 66 | MongoritoScheme: 'adonis-mongorito/AuthManager/MongoritoScheme' // and this line 67 | } 68 | ``` 69 | 70 | ## Usage 71 | 72 | *Now you can create Mongo MODELS like Lucid Models* 73 | 74 | **app/Model/Post.js** 75 | 76 | ```js 77 | 'use strict' 78 | 79 | const MongoritoModel = use('MongoritoModel') 80 | 81 | class Post extends MongoritoModel { 82 | 83 | } 84 | 85 | module.exports = Post 86 | ``` 87 | 88 | *And use like this:* 89 | 90 | **app/Http/routes.js** 91 | 92 | ```js 93 | 'use strict' 94 | 95 | const Route = use('Route') 96 | 97 | const Post = use('App/Model/Post') 98 | 99 | Route.get('/posts', function * (request, response) { 100 | //Simple get All Posts 101 | const posts = yield Post.all() 102 | 103 | response.json(posts) 104 | }) 105 | 106 | Route.post('/posts', function * (request, response) { 107 | 108 | //create new Post 109 | let post = new Post({ 110 | title: request.input('title'), 111 | author: { 112 | name: request.input('author_name') 113 | } 114 | }); 115 | 116 | yield post.save(); 117 | 118 | response.json(post) 119 | }) 120 | ``` 121 | 122 | **As you can see, it's very easy to use.** 123 | **** 124 | 125 | ## How to use Mongorito stuff? 126 | - Mongorito Official site/docs: https://mongorito.com 127 | - Mongorito Repository: https://github.com/vdemedes/mongorito 128 | 129 | ## Found any Bugs? 130 | Please before open a Issue on this repository, 131 | check if it's not a bug on Mongorito package here: **https://github.com/vdemedes/mongorito/issues** 132 | 133 | 134 | ## License 135 | 136 | Adonis-Mongorito is released under the MIT License. 137 | 138 | -------------------------------------------------------------------------------- /Serializer/MongoritoSerializer.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Ioc = require('adonis-fold').Ioc; 4 | 5 | class MongoritoSerializer { 6 | 7 | constructor () { 8 | const Hash = use('Hash') 9 | this.hash = Hash 10 | } 11 | 12 | /** 13 | * dependencies to be auto injected by the IoC container 14 | * @return {Array} 15 | * @private 16 | */ 17 | static get inject () { 18 | return ['Adonis/Src/Hash'] 19 | } 20 | 21 | /** 22 | * returns primaryKey to be used for saving sessions 23 | * 24 | * @param {Object} options 25 | * @return {String} 26 | * 27 | * @public 28 | */ 29 | primaryKey(authenticatorOptions) { 30 | var obj = {primaryKey: '_id'} 31 | return obj.primaryKey 32 | } 33 | /** 34 | * returns the model from the Ioc container if parameter 35 | * is a string, otherwise returns the actual binding. 36 | * 37 | * @param {String|Object} model 38 | * @return {Object} 39 | * @throws Error when unable to find binding from the IoC container. 40 | * 41 | * @private 42 | */ 43 | _getModel (model) { 44 | return typeof (model) === 'string' ? Ioc.use(model) : model 45 | } 46 | 47 | /** 48 | * decorates database query object by passing options 49 | * query to where object. 50 | * 51 | * @param {Object} query 52 | * @param {Object} options 53 | * 54 | * @private 55 | */ 56 | _decorateQuery (query, options) { 57 | if (options.query) { 58 | query.andWhere(options.query) 59 | } 60 | } 61 | 62 | /** 63 | * returns the model instance by model primary key 64 | * 65 | * @param {Number} id 66 | * @param {Object} options - Options defined as the config 67 | * @return {Object} 68 | * 69 | * @public 70 | */ 71 | * findById (id, options) { 72 | const model = this._getModel(options.model) 73 | return yield model.find(id) 74 | } 75 | 76 | /** 77 | * returns model instance using the user credentials 78 | * 79 | * @param {String} email 80 | * @param {Object} options - Options defined as the config 81 | * @return {Object} 82 | * 83 | * @public 84 | */ 85 | * findByCredentials (email, options) { 86 | const model = this._getModel(options.model) 87 | const query = model.where(options.uid, email) 88 | this._decorateQuery(query, options) 89 | const row = yield query.find() 90 | return row.length > 0 ? row[0] : null 91 | } 92 | 93 | /** 94 | * finds a token using token model and it's related user. 95 | * It is important to set a belongsTo relation with the 96 | * user model. 97 | * 98 | * @param {String} token 99 | * @param {Object} options 100 | * @return {Object} 101 | * 102 | * @public 103 | */ 104 | * findByToken (token, options) { 105 | const model = this._getModel(options.model) 106 | const query = model.query().where('token', token).andWhere('is_revoked', false) 107 | this._decorateQuery(query, options) 108 | return yield query.with('user').first() 109 | } 110 | 111 | /** 112 | * return user for a given token 113 | * 114 | * @param {Object} token 115 | * @return {Object} 116 | * 117 | * @public 118 | */ 119 | * getUserForToken (token) { 120 | // since user is eagerLoaded with token, we just need 121 | // to pull the user out of it. 122 | return token.get('user') 123 | } 124 | 125 | /** 126 | * makes token expiry date by adding milliseconds 127 | * to the current date. 128 | * 129 | * @param {Number} expiry 130 | * @return {Date} 131 | * 132 | * @private 133 | */ 134 | _getTokenExpiryDate (expiry) { 135 | return new Date(Date.now() + expiry) 136 | } 137 | 138 | /** 139 | * saves a new token for a given user. 140 | * 141 | * @param {Object} user 142 | * @param {String} token 143 | * @param {Object} options 144 | * @param {Number} expiry 145 | * @returns {Object} - Saved token instance 146 | * 147 | * @public 148 | */ 149 | * saveToken (user, token, options, expiry) { 150 | const tokenObject = { 151 | token: token, 152 | forever: !expiry, 153 | expiry: expiry ? this._getTokenExpiryDate(expiry) : null, 154 | is_revoked: false 155 | } 156 | const Token = this._getModel(options.model) 157 | const tokenInstance = new Token(tokenObject) 158 | const isSaved = yield user.apiTokens().save(tokenInstance) 159 | return isSaved ? tokenInstance : null 160 | } 161 | 162 | /** 163 | * revokes tokens for a given user. 164 | * 165 | * @param {Object} user 166 | * @param {Array} tokens 167 | * @param {Boolean} reverse 168 | * @returns {Number} - Number of affected rows 169 | * 170 | * @public 171 | */ 172 | * revokeTokens (user, tokens, reverse) { 173 | const userTokens = user.apiTokens() 174 | if (tokens) { 175 | const method = reverse ? 'whereNotIn' : 'whereIn' 176 | userTokens[method]('token', tokens) 177 | } 178 | return yield userTokens.update({'is_revoked': true}) 179 | } 180 | 181 | /** 182 | * validates a token by making user a user for the corresponding 183 | * token exists and the token has not been expired. 184 | * 185 | * @param {Object} token - token model resolved from findByToken 186 | * @param {Object} options 187 | * @return {Boolean} 188 | * 189 | * @public 190 | */ 191 | * validateToken (token, options) { 192 | /** 193 | * return false when token or the user related to token 194 | * does not exists. 195 | */ 196 | if (!token || !token.get || !token.get('user')) { 197 | return false 198 | } 199 | 200 | /** 201 | * return the user when token life is set to forever 202 | */ 203 | if (token.forever) { 204 | return true 205 | } 206 | 207 | /** 208 | * check whether the expiry date is over the current 209 | * date/time 210 | */ 211 | const expiry = token.toJSON().expiry 212 | return util.dateDiff(new Date(), new Date(expiry)) > 0 213 | } 214 | 215 | /** 216 | * validates user crendentials using the model instance and 217 | * the password. It makes use of Hash provider. 218 | * 219 | * @param {Object} user 220 | * @param {String} password 221 | * @param {Object} options 222 | * @return {Boolean} 223 | * 224 | * @public 225 | */ 226 | * validateCredentials (user, password, options) { 227 | if (!user || !user.attributes || !user.attributes[options.password]) { 228 | return false 229 | } 230 | const actualPassword = user.attributes[options.password] 231 | try { 232 | let compare = yield this.hash.verify(password, actualPassword) 233 | return compare 234 | } catch (e) { 235 | return false 236 | } 237 | } 238 | 239 | } 240 | 241 | module.exports = MongoritoSerializer 242 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "adonis-mongorito", 3 | "version": "0.0.2", 4 | "description": "A package to use AdonisJS with MongoDB(using mongorito package)", 5 | "main": "index.js", 6 | "scripts": { 7 | "lint": "standard src/**/*.js src/**/**/*.js src/**/**/**/*.js lib/*.js test/**/*.js providers/*.js", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/allanfreitas/adonis-mongorito.git" 13 | }, 14 | "devDependencies": { 15 | "adonis-fold": "^3.0.3", 16 | "semantic-release": "^4.3.5", 17 | "standard": "^8.0.0" 18 | }, 19 | "dependencies": { 20 | "cat-log": "^1.0.2", 21 | "mongorito": "^2.1.2" 22 | }, 23 | "config": { 24 | "flags": "--harmony_proxies" 25 | }, 26 | "keywords": [ 27 | "adonisjs", 28 | "adonis", 29 | "mongodb", 30 | "mongorito" 31 | ], 32 | "author": "Allan Freitas (https://tech.allanfreitas.com.br)", 33 | "license": "MIT", 34 | "bugs": { 35 | "url": "https://github.com/allanfreitas/adonis-mongorito/issues" 36 | }, 37 | "homepage": "https://github.com/allanfreitas/adonis-mongorito#readme" 38 | } 39 | -------------------------------------------------------------------------------- /providers/MongoritoProvider.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Ioc = require('adonis-fold').Ioc 4 | const ServiceProvider = require('adonis-fold').ServiceProvider 5 | const Mongorito = require('mongorito') 6 | const CatLog = require('cat-log') 7 | const logger = new CatLog('adonis:mongorito') 8 | const MongoritoScheme = require('../AuthManager/MongoritoScheme') 9 | const MongoritoSerializer = require('../Serializer/MongoritoSerializer') 10 | 11 | class MongoritoModel extends Mongorito.Model { 12 | 13 | /** 14 | * Required to instructor IoC container to never make an 15 | * instance of this class even when `make` is called. 16 | * Model instances should be controlled by user. 17 | * 18 | * @return {Boolean} 19 | */ 20 | static get makePlain () { 21 | return true 22 | } 23 | 24 | } 25 | 26 | class MongoritoProvider extends ServiceProvider { 27 | 28 | * register () { 29 | const managers = this.app.getManagers() 30 | 31 | // Add Mongo auth support 32 | managers['Adonis/Src/AuthManager'].extend('MongoritoScheme', MongoritoScheme, 'scheme') 33 | // Add Mongo serializer 34 | Ioc.extend('Adonis/Src/AuthManager', 'MongoritoSerializer', function (app) { 35 | return new MongoritoSerializer() 36 | }, 'serializer') 37 | 38 | this.app.singleton('Adonis/Addons/MongoritoModel', function (app) { 39 | const Config = app.use('Adonis/Src/Config') 40 | const mongoHost = Config.get('mongo.host') 41 | const mongoPort = Config.get('mongo.port') 42 | const mongoDb = Config.get('mongo.db') 43 | const mongoUser = Config.get('mongo.user', '') 44 | const mongoPass = Config.get('mongo.pass', '') 45 | const mongoOptions = Config.get('mongo.options', '') 46 | 47 | var connectUri = `${mongoHost}:${mongoPort}/${mongoDb}` 48 | if (mongoOptions !== '') { 49 | connectUri += '?' + mongoOptions 50 | } 51 | const connectionString = (mongoUser !== '' || mongoPass !== '') ? `${mongoUser}:${mongoPass}@${connectUri}` : connectUri 52 | 53 | logger.verbose('connection string %s', connectionString) 54 | Mongorito.connect(connectionString) 55 | 56 | return MongoritoModel 57 | }) 58 | } 59 | } 60 | 61 | module.exports = MongoritoProvider 62 | --------------------------------------------------------------------------------