From 4b222c9921cdf4316ddfe56045a377d78a1c73df Mon Sep 17 00:00:00 2001 From: Sockenklaus Date: Sun, 17 Oct 2021 17:11:21 +0200 Subject: [PATCH] added login / logout functionality and authorization rules via bouncer --- .adonisrc.json | 9 +- ace-manifest.json | 36 ++ app/Controllers/Http/AuthController.ts | 22 ++ app/Controllers/Http/EmployeesController.ts | 16 +- app/Models/User.ts | 6 + contracts/bouncer.ts | 16 + database/migrations/1634474414234_users.ts | 23 ++ database/seeders/Employee.ts | 31 ++ database/seeders/User.ts | 21 + package-lock.json | 418 +++++++++++++++++++- package.json | 2 + start/bouncer.ts | 67 ++++ start/routes.ts | 13 +- tsconfig.json | 3 +- 14 files changed, 665 insertions(+), 18 deletions(-) create mode 100644 app/Controllers/Http/AuthController.ts create mode 100644 contracts/bouncer.ts create mode 100644 database/migrations/1634474414234_users.ts create mode 100644 database/seeders/Employee.ts create mode 100644 database/seeders/User.ts create mode 100644 start/bouncer.ts diff --git a/.adonisrc.json b/.adonisrc.json index 6b4e0c5..c1edcc9 100644 --- a/.adonisrc.json +++ b/.adonisrc.json @@ -4,7 +4,8 @@ "./commands", "@adonisjs/core/build/commands/index.js", "@adonisjs/repl/build/commands", - "@adonisjs/lucid/build/commands" + "@adonisjs/lucid/build/commands", + "@adonisjs/bouncer/build/commands" ], "exceptionHandlerNamespace": "App/Exceptions/Handler", "aliases": { @@ -15,14 +16,16 @@ }, "preloads": [ "./start/routes", - "./start/kernel" + "./start/kernel", + "./start/bouncer" ], "providers": [ "./providers/AppProvider", "@adonisjs/core", "@adonisjs/lucid", "@adonisjs/session", - "@adonisjs/auth" + "@adonisjs/auth", + "@adonisjs/bouncer" ], "aceProviders": [ "@adonisjs/repl" diff --git a/ace-manifest.json b/ace-manifest.json index 79eaee7..542704c 100644 --- a/ace-manifest.json +++ b/ace-manifest.json @@ -261,6 +261,42 @@ "alias": "c" } ] + }, + "make:policy": { + "settings": {}, + "commandPath": "@adonisjs/bouncer/build/commands/MakePolicy", + "commandName": "make:policy", + "description": "Make a new bouncer policy", + "args": [ + { + "type": "string", + "propertyName": "name", + "name": "name", + "required": true, + "description": "Name of the policy to create" + } + ], + "aliases": [], + "flags": [ + { + "name": "resource-model", + "propertyName": "resourceModel", + "type": "string", + "description": "Name of the resource model to authorize" + }, + { + "name": "user-model", + "propertyName": "userModel", + "type": "string", + "description": "Name of the user model to be authorized" + }, + { + "name": "actions", + "propertyName": "actions", + "type": "array", + "description": "Actions to implement" + } + ] } }, "aliases": {} diff --git a/app/Controllers/Http/AuthController.ts b/app/Controllers/Http/AuthController.ts new file mode 100644 index 0000000..574714d --- /dev/null +++ b/app/Controllers/Http/AuthController.ts @@ -0,0 +1,22 @@ +import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext' + +export default class AuthController { + + public async login({auth, request, response}: HttpContextContract) { + const username = request.input('username') + const password = request.input('password') + + try { + await auth.attempt(username, password) + response.ok("Login successful") + } catch (error) { + return error + } + } + + public async logout({auth, response}: HttpContextContract) { + await auth.logout() + + return response.ok('Logged out successfully') + } +} diff --git a/app/Controllers/Http/EmployeesController.ts b/app/Controllers/Http/EmployeesController.ts index 5eca9f2..97576d3 100644 --- a/app/Controllers/Http/EmployeesController.ts +++ b/app/Controllers/Http/EmployeesController.ts @@ -7,10 +7,8 @@ import Database from '@ioc:Adonis/Lucid/Database' // TODO: #1 Implement paginator for Employee-Index export default class EmployeesController { - public async index ({}: HttpContextContract) { - const report = Database.manager.report() - - console.log(report) + public async index ({bouncer}: HttpContextContract) { + await bouncer.authorize('employees.index') return await Database.from('employees').select('*') } @@ -35,8 +33,14 @@ export default class EmployeesController { } - public async show ({params}: HttpContextContract) { - return await Employee.find(params.id) + public async show ({params, bouncer}: HttpContextContract) { + const emp = await Employee.findOrFail(params.id) + + if (await bouncer.denies('employees.show', emp)){ + return 'Not admin or wrong user' + } + + return emp } public async update ({params, request}: HttpContextContract) { diff --git a/app/Models/User.ts b/app/Models/User.ts index 399e38b..d13b70c 100644 --- a/app/Models/User.ts +++ b/app/Models/User.ts @@ -25,6 +25,12 @@ export default class User extends BaseModel { @hasOne(() => Employee) public employeeProfile : HasOne + @column() + public role : string + + @column() + public isActive : boolean + @beforeSave() public static async hashPassword(user: User) { if(user.$dirty.password){ diff --git a/contracts/bouncer.ts b/contracts/bouncer.ts new file mode 100644 index 0000000..57c5ec2 --- /dev/null +++ b/contracts/bouncer.ts @@ -0,0 +1,16 @@ +/** + * Contract source: https://git.io/Jte3v + * + * Feel free to let us know via PR, if you find something broken in this config + * file. + */ + +import { actions, policies } from '../start/bouncer' + +declare module '@ioc:Adonis/Addons/Bouncer' { + type ApplicationActions = ExtractActionsTypes + type ApplicationPolicies = ExtractPoliciesTypes + + interface ActionsList extends ApplicationActions {} + interface PoliciesList extends ApplicationPolicies {} +} diff --git a/database/migrations/1634474414234_users.ts b/database/migrations/1634474414234_users.ts new file mode 100644 index 0000000..6ec5260 --- /dev/null +++ b/database/migrations/1634474414234_users.ts @@ -0,0 +1,23 @@ +import Hash from '@ioc:Adonis/Core/Hash' +import BaseSchema from '@ioc:Adonis/Lucid/Schema' +import User from 'App/Models/User' + +export default class Users extends BaseSchema { + protected tableName = 'users' + + public async up () { + this.schema.alterTable(this.tableName, (table) => { + table.boolean('is_active').defaultTo(false) + table.string('role') + .defaultTo('employee') + .notNullable() + }) + } + + public async down () { + this.schema.alterTable(this.tableName, (table) => { + table.dropColumn('is_active') + table.dropColumn('role') + }) + } +} diff --git a/database/seeders/Employee.ts b/database/seeders/Employee.ts new file mode 100644 index 0000000..576fc2b --- /dev/null +++ b/database/seeders/Employee.ts @@ -0,0 +1,31 @@ +import BaseSeeder from '@ioc:Adonis/Lucid/Seeder' +import Logger from '@ioc:Adonis/Core/Logger' +import Employee from 'App/Models/Employee' + +export default class EmployeeSeeder extends BaseSeeder { + public async run () { + + try { + await Employee.createMany([ + { + firstName: 'Pascal', + lastName: 'König', + shorthand: 'PK' + }, + { + firstName: 'Sandra', + lastName: 'Sühl', + shorthand: 'SS' + }, + { + firstName: 'Karin', + lastName: 'Behr', + shorthand: 'KB' + } + ]) + } catch (error) { + Logger.error(error) + } + + } +} diff --git a/database/seeders/User.ts b/database/seeders/User.ts new file mode 100644 index 0000000..ce1ed11 --- /dev/null +++ b/database/seeders/User.ts @@ -0,0 +1,21 @@ +import BaseSeeder from '@ioc:Adonis/Lucid/Seeder' +import User from 'App/Models/User' +import Logger from '@ioc:Adonis/Core/Logger' + +export default class UserSeeder extends BaseSeeder { + public async run () { + try { + await User.create({ + username: 'admin', + password: 'admin', + role: 'admin', + email: 'test@test.de', + isActive: true + }) + } + catch(error) { + Logger.error(error.message) + } + } +} + diff --git a/package-lock.json b/package-lock.json index bf0b77b..d27d372 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,11 +9,13 @@ "version": "1.0.0", "dependencies": { "@adonisjs/auth": "^8.0.10", + "@adonisjs/bouncer": "^2.2.5", "@adonisjs/core": "^5.4.0", "@adonisjs/lucid": "^16.2.1", "@adonisjs/repl": "^3.1.6", "@adonisjs/session": "^6.1.2", "luxon": "^2.0.2", + "phc-argon2": "^1.1.2", "proxy-addr": "^2.0.7", "reflect-metadata": "^0.1.13", "source-map-support": "^0.5.20", @@ -144,6 +146,27 @@ "@adonisjs/http-server": "^5.0.0" } }, + "node_modules/@adonisjs/bouncer": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/@adonisjs/bouncer/-/bouncer-2.2.5.tgz", + "integrity": "sha512-f060bMQwjJBAQ2pTtUrCbJ3n3/J3nQvzdrpWpWSEzdn8bdZALGh2R1nIhH0cVh8lMQuXzowo9ojxjWGFI0LfmQ==", + "dependencies": { + "@poppinss/utils": "^3.2.0" + }, + "peerDependencies": { + "@adonisjs/auth": "^8.0.0", + "@adonisjs/core": "^5.1.0", + "@adonisjs/view": "^6.0.0" + }, + "peerDependenciesMeta": { + "@adonisjs/auth": { + "optional": true + }, + "@adonisjs/view": { + "optional": true + } + } + }, "node_modules/@adonisjs/config": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/@adonisjs/config/-/config-3.0.5.tgz", @@ -563,6 +586,121 @@ "integrity": "sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w==", "dev": true }, + "node_modules/@kdf/salt": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@kdf/salt/-/salt-2.0.1.tgz", + "integrity": "sha512-1RBY7HcGYuWBm0+4ygjdRerN+mhpuT5picGB6+azqUXsz/IZljegrKkeHRiV6wuxY8n4HrxOuw8ou7JuGxRWdQ==", + "engines": { + "node": ">=10" + } + }, + "node_modules/@mapbox/node-pre-gyp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.5.tgz", + "integrity": "sha512-4srsKPXWlIxp5Vbqz5uLfBN+du2fJChBoYn/f2h991WLdk7jUvcSk/McVLSv/X+xQIPI8eGD5GjrnygdyHnhPA==", + "dependencies": { + "detect-libc": "^1.0.3", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.1", + "nopt": "^5.0.0", + "npmlog": "^4.1.2", + "rimraf": "^3.0.2", + "semver": "^7.3.4", + "tar": "^6.1.0" + }, + "bin": { + "node-pre-gyp": "bin/node-pre-gyp" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "engines": { + "node": ">=10" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/minipass": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.5.tgz", + "integrity": "sha512-+8NzxD82XQoNKNrl1d/FSi+X8wAEWR+sbYAfIvub4Nz0d22plFG72CEVVaufV8PNf4qSslFTD8VMOxNVhHCjTw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/tar": { + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", + "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, "node_modules/@mrmlnc/readdir-enhanced": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz", @@ -1277,6 +1415,17 @@ "integrity": "sha512-cQH/NP250gOF9k3TTDhVsTOPSAvyH4MhKVZ4ryYiihA+vnP27sut1gVIrRas3Evl5d2wEgWVGI5DgdP/ZFSk0w==", "dev": true }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/aggregate-error": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", @@ -1421,6 +1570,21 @@ "safe-buffer": "~5.1.0" } }, + "node_modules/argon2": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/argon2/-/argon2-0.28.2.tgz", + "integrity": "sha512-8oRk3kPlL0lLletENzhpbF9zoZJqvIHwTkjBseMrg1uD4gBMqhqnjJz1z3lEtwT0oqQAEkEwsEpsjaQBBRHcWw==", + "hasInstallScript": true, + "dependencies": { + "@mapbox/node-pre-gyp": "^1.0.1", + "@phc/format": "^1.0.0", + "node-addon-api": "^3.0.2", + "opencollective-postinstall": "^2.0.3" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -3996,6 +4160,18 @@ "npm": ">=1.3.7" } }, + "node_modules/https-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", @@ -4696,7 +4872,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, "dependencies": { "semver": "^6.0.0" }, @@ -4711,7 +4886,6 @@ "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, "bin": { "semver": "bin/semver.js" } @@ -5226,6 +5400,17 @@ "lodash": "^4.17.21" } }, + "node_modules/node-fetch": { + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.5.tgz", + "integrity": "sha512-mmlIVHJEu5rnIxgEgez6b9GgWXbkZj5YZ7fx+2r94a2E+Uirsp6HsPTPlomfdHtpt/B0cdKviwkoaM6pyvUOpQ==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + } + }, "node_modules/node-gyp": { "version": "3.8.0", "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz", @@ -5669,6 +5854,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/opencollective-postinstall": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz", + "integrity": "sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==", + "bin": { + "opencollective-postinstall": "index.js" + } + }, "node_modules/optionator": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", @@ -5995,6 +6188,20 @@ "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.5.0.tgz", "integrity": "sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ==" }, + "node_modules/phc-argon2": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/phc-argon2/-/phc-argon2-1.1.2.tgz", + "integrity": "sha512-R+1G4/lZHPQrLKiYBSio7xhU0vhfP5mxLYJBuWWiGAtynorRM4QtQGZcvnT4BTaRZOUaCaqlR4PC8cBrQmxZ3A==", + "dependencies": { + "@kdf/salt": "^2.0.1", + "@phc/format": "^1.0.0", + "argon2": "^0.28.2", + "tsse": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/picomatch": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", @@ -6591,7 +6798,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, "dependencies": { "glob": "^7.1.3" }, @@ -7686,6 +7892,11 @@ "node": ">=0.8" } }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" + }, "node_modules/truncatise": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/truncatise/-/truncatise-0.0.8.tgz", @@ -7696,6 +7907,17 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" }, + "node_modules/tsse": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tsse/-/tsse-2.0.0.tgz", + "integrity": "sha512-KiYDxhCTbCUQWXtCSF2OWafC71C/ZUZP6TyCh/w7nPjzY1ZLY70KtDSfmePs3H4vSdxQotqdJvAkGAofxsQ05w==", + "dependencies": { + "safe-buffer": "^5.1.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", @@ -7977,6 +8199,11 @@ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", "optional": true }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" + }, "node_modules/webpack-merge": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.2.2.tgz", @@ -7986,6 +8213,15 @@ "lodash": "^4.17.15" } }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -8153,6 +8389,14 @@ "media-typer": "^1.1.0" } }, + "@adonisjs/bouncer": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/@adonisjs/bouncer/-/bouncer-2.2.5.tgz", + "integrity": "sha512-f060bMQwjJBAQ2pTtUrCbJ3n3/J3nQvzdrpWpWSEzdn8bdZALGh2R1nIhH0cVh8lMQuXzowo9ojxjWGFI0LfmQ==", + "requires": { + "@poppinss/utils": "^3.2.0" + } + }, "@adonisjs/config": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/@adonisjs/config/-/config-3.0.5.tgz", @@ -8507,6 +8751,90 @@ "integrity": "sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w==", "dev": true }, + "@kdf/salt": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@kdf/salt/-/salt-2.0.1.tgz", + "integrity": "sha512-1RBY7HcGYuWBm0+4ygjdRerN+mhpuT5picGB6+azqUXsz/IZljegrKkeHRiV6wuxY8n4HrxOuw8ou7JuGxRWdQ==" + }, + "@mapbox/node-pre-gyp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.5.tgz", + "integrity": "sha512-4srsKPXWlIxp5Vbqz5uLfBN+du2fJChBoYn/f2h991WLdk7jUvcSk/McVLSv/X+xQIPI8eGD5GjrnygdyHnhPA==", + "requires": { + "detect-libc": "^1.0.3", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.1", + "nopt": "^5.0.0", + "npmlog": "^4.1.2", + "rimraf": "^3.0.2", + "semver": "^7.3.4", + "tar": "^6.1.0" + }, + "dependencies": { + "chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==" + }, + "fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.5.tgz", + "integrity": "sha512-+8NzxD82XQoNKNrl1d/FSi+X8wAEWR+sbYAfIvub4Nz0d22plFG72CEVVaufV8PNf4qSslFTD8VMOxNVhHCjTw==", + "requires": { + "yallist": "^4.0.0" + } + }, + "minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "requires": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + } + }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" + }, + "nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "requires": { + "abbrev": "1" + } + }, + "tar": { + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", + "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", + "requires": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + } + }, "@mrmlnc/readdir-enhanced": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz", @@ -9042,6 +9370,14 @@ "integrity": "sha512-cQH/NP250gOF9k3TTDhVsTOPSAvyH4MhKVZ4ryYiihA+vnP27sut1gVIrRas3Evl5d2wEgWVGI5DgdP/ZFSk0w==", "dev": true }, + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "requires": { + "debug": "4" + } + }, "aggregate-error": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", @@ -9156,6 +9492,17 @@ } } }, + "argon2": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/argon2/-/argon2-0.28.2.tgz", + "integrity": "sha512-8oRk3kPlL0lLletENzhpbF9zoZJqvIHwTkjBseMrg1uD4gBMqhqnjJz1z3lEtwT0oqQAEkEwsEpsjaQBBRHcWw==", + "requires": { + "@mapbox/node-pre-gyp": "^1.0.1", + "@phc/format": "^1.0.0", + "node-addon-api": "^3.0.2", + "opencollective-postinstall": "^2.0.3" + } + }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -11168,6 +11515,15 @@ "sshpk": "^1.7.0" } }, + "https-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "requires": { + "agent-base": "6", + "debug": "4" + } + }, "human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", @@ -11687,7 +12043,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, "requires": { "semver": "^6.0.0" }, @@ -11695,8 +12050,7 @@ "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" } } }, @@ -12111,6 +12465,14 @@ "lodash": "^4.17.21" } }, + "node-fetch": { + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.5.tgz", + "integrity": "sha512-mmlIVHJEu5rnIxgEgez6b9GgWXbkZj5YZ7fx+2r94a2E+Uirsp6HsPTPlomfdHtpt/B0cdKviwkoaM6pyvUOpQ==", + "requires": { + "whatwg-url": "^5.0.0" + } + }, "node-gyp": { "version": "3.8.0", "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz", @@ -12454,6 +12816,11 @@ "is-wsl": "^2.2.0" } }, + "opencollective-postinstall": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz", + "integrity": "sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==" + }, "optionator": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", @@ -12701,6 +13068,17 @@ "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.5.0.tgz", "integrity": "sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ==" }, + "phc-argon2": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/phc-argon2/-/phc-argon2-1.1.2.tgz", + "integrity": "sha512-R+1G4/lZHPQrLKiYBSio7xhU0vhfP5mxLYJBuWWiGAtynorRM4QtQGZcvnT4BTaRZOUaCaqlR4PC8cBrQmxZ3A==", + "requires": { + "@kdf/salt": "^2.0.1", + "@phc/format": "^1.0.0", + "argon2": "^0.28.2", + "tsse": "^2.0.0" + } + }, "picomatch": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", @@ -13152,7 +13530,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, "requires": { "glob": "^7.1.3" } @@ -14003,6 +14380,11 @@ "punycode": "^2.1.1" } }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" + }, "truncatise": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/truncatise/-/truncatise-0.0.8.tgz", @@ -14013,6 +14395,14 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" }, + "tsse": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tsse/-/tsse-2.0.0.tgz", + "integrity": "sha512-KiYDxhCTbCUQWXtCSF2OWafC71C/ZUZP6TyCh/w7nPjzY1ZLY70KtDSfmePs3H4vSdxQotqdJvAkGAofxsQ05w==", + "requires": { + "safe-buffer": "^5.1.1" + } + }, "tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", @@ -14235,6 +14625,11 @@ } } }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" + }, "webpack-merge": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.2.2.tgz", @@ -14244,6 +14639,15 @@ "lodash": "^4.17.15" } }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/package.json b/package.json index 6e76aa2..abe3be6 100644 --- a/package.json +++ b/package.json @@ -24,11 +24,13 @@ }, "dependencies": { "@adonisjs/auth": "^8.0.10", + "@adonisjs/bouncer": "^2.2.5", "@adonisjs/core": "^5.4.0", "@adonisjs/lucid": "^16.2.1", "@adonisjs/repl": "^3.1.6", "@adonisjs/session": "^6.1.2", "luxon": "^2.0.2", + "phc-argon2": "^1.1.2", "proxy-addr": "^2.0.7", "reflect-metadata": "^0.1.13", "source-map-support": "^0.5.20", diff --git a/start/bouncer.ts b/start/bouncer.ts new file mode 100644 index 0000000..947c7bb --- /dev/null +++ b/start/bouncer.ts @@ -0,0 +1,67 @@ +/** + * Contract source: https://git.io/Jte3T + * + * Feel free to let us know via PR, if you find something broken in this config + * file. + */ + +import Bouncer from '@ioc:Adonis/Addons/Bouncer' +import User from 'App/Models/User' +import Employee from 'App/Models/Employee' + +/* +|-------------------------------------------------------------------------- +| Bouncer Actions +|-------------------------------------------------------------------------- +| +| Actions allows you to separate your application business logic from the +| authorization logic. Feel free to make use of policies when you find +| yourself creating too many actions +| +| You can define an action using the `.define` method on the Bouncer object +| as shown in the following example +| +| ``` +| Bouncer.define('deletePost', (user: User, post: Post) => { +| return post.user_id === user.id +| }) +| ``` +| +|**************************************************************** +| NOTE: Always export the "actions" const from this file +|**************************************************************** +*/ +export const { actions } = Bouncer + + .define('employees.index', (user: User) => { + return user.role === 'admin' + }) + + .define('employees.show', (user: User, employee : Employee) => { + return user.role === 'admin' || user.id === employee.userId + }) + +/* +|-------------------------------------------------------------------------- +| Bouncer Policies +|-------------------------------------------------------------------------- +| +| Policies are self contained actions for a given resource. For example: You +| can create a policy for a "User" resource, one policy for a "Post" resource +| and so on. +| +| The "registerPolicies" accepts a unique policy name and a function to lazy +| import the policy +| +| ``` +| Bouncer.registerPolicies({ +| UserPolicy: () => import('App/Policies/User'), +| PostPolicy: () => import('App/Policies/Post') +| }) +| ``` +| +|**************************************************************** +| NOTE: Always export the "policies" const from this file +|**************************************************************** +*/ +export const { policies } = Bouncer.registerPolicies({}) diff --git a/start/routes.ts b/start/routes.ts index df1dae9..ba42632 100644 --- a/start/routes.ts +++ b/start/routes.ts @@ -20,4 +20,15 @@ import Route from '@ioc:Adonis/Core/Route' -Route.resource('employees', 'EmployeesController').except(['create', 'edit']) +Route.group(() => { + Route.post('login', 'AuthController.login').as('login') +}) +.prefix('api/v1') + +Route.group(() => { + Route.get('logout', 'AuthController.logout').as('logout') + + Route.resource('employees', 'EmployeesController').apiOnly() +}) +.prefix('api/v1') +.middleware('auth') diff --git a/tsconfig.json b/tsconfig.json index 7f03bb1..9db8b70 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -30,7 +30,8 @@ "@adonisjs/repl", "@adonisjs/lucid", "@adonisjs/session", - "@adonisjs/auth" + "@adonisjs/auth", + "@adonisjs/bouncer" ] } }