├── .gitignore ├── README.md ├── example ├── acl-config.js ├── index.bundle.js ├── index.html ├── index.js └── webpack.config.js ├── gulpfile.js ├── package.json └── src ├── authorize.js ├── authorize ├── permission.js └── role.js ├── component.js ├── directive.js ├── index.js ├── options.js ├── promise.js └── utils.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vue-authorize 2 | 3 | [![Join the chat at https://gitter.im/vuejs-auth/vue-authorize](https://badges.gitter.im/vue-authenticate/Lobby.svg)](https://gitter.im/vuejs-auth/vue-authorize?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 4 | 5 | **vue-authorize** is a simple authorization library for [Vue.js](https://vuejs.org/) and is an extension of [vue-authenticate](https://github.com/dgrubelic/vue-authenticate) library. 6 | 7 | **DISCLAIMER** 8 | 9 | For now, this package only supports ES6 import usage, but soon will have standalone ES5 build. 10 | 11 | 12 | *DEMO app comming soon...* 13 | 14 | 15 | ## Instalation 16 | ```bash 17 | npm install vue-authorize 18 | ``` 19 | 20 | ## Usage 21 | ```javascript 22 | import Vue from 'vue' 23 | import VueResource from 'vue-resource' 24 | import VueAuthenticate from 'vue-authenticate' 25 | import VueAuthorize from 'vue-authorize' 26 | 27 | Vue.use(VueResource) 28 | Vue.use(VueAuthenticate, { 29 | // Your authentication config 30 | } 31 | 32 | Vue.use(VueAuthorize, { 33 | roles: { 34 | user: { 35 | handler: function () { 36 | // You have $auth instance directly in your role or permission handlers 37 | return this.$auth.isAuthorized() 38 | }, 39 | permissions: ['can_read', 'can_create', 'can_update', 'can_delete'] 40 | }, 41 | guest: { 42 | handler: function () { 43 | return !this.$auth.isAuthorized() 44 | }, 45 | permissions: ['can_read'] 46 | } 47 | }, 48 | 49 | permissions: { 50 | // You can have simple logic for checking if current user has write rights 51 | can_read: function () { return true }, 52 | 53 | // ... or you can even perform AJAX request and return Promise instance 54 | can_create: function () { return Promise.resolve() }, 55 | can_update: function () { return false }, 56 | can_delete: function () { return Promise.reject() } 57 | } 58 | }) 59 | ``` 60 | 61 | For internal Vue component usage, you have access to `$authorize` service. To check if user has access to some resource, you can simply call `$authorize.isAuthorized(roles, permissions)` method. 62 | 63 | ```javascript 64 | new Vue({ 65 | data() { 66 | return { 67 | showCreateButton: false, 68 | showUpdateButton: false, 69 | showDeleteButton: false 70 | } 71 | }, 72 | 73 | inserted () { 74 | /** 75 | * This will resolve since user role (if we asume that use is authenticated) 76 | * has 'can_create' permission. 77 | */ 78 | this.$authorize.isAuthorized(['user'], ['can_create']).then(() => { 79 | // User is allowed to add more resources 80 | this.showCreateButton = true 81 | }).catch(() => { 82 | // User is not allowed to add any more resources 83 | this.showCreateButton = false 84 | }) 85 | 86 | /** 87 | * This will reject since user role (if we asume that use is authenticated) 88 | * has 'can_update' permission, but that permission returns `false`. 89 | */ 90 | this.$authorize.isAuthorized(['guest'], ['can_update']).then(() => { 91 | // User is allowed to add more resources 92 | this.showUpdateButton = true 93 | }).catch(() => { 94 | // User is not allowed to add any more resources 95 | this.showUpdateButton = false 96 | }) 97 | 98 | /** 99 | * This will reject since user role (if we asume that use is authenticated) 100 | * has 'can_delete' permission, but that permission returns `Promise.reject()`. 101 | */ 102 | this.$authorize.isAuthorized(['guest'], ['can_delete']).then(() => { 103 | // User is allowed to add more resources 104 | this.showUpdateButton = true 105 | }).catch(() => { 106 | // User is not allowed to add any more resources 107 | this.showUpdateButton = false 108 | }) 109 | 110 | /** 111 | * This will reject since guest role (if we asume that use is authenticated) 112 | * does not have 'can_create' permission. 113 | */ 114 | this.$authorize.isAuthorized(['guest'], ['can_create']).then(() => { 115 | // User is allowed to add more resources 116 | this.showCreateButton = true 117 | }).catch(() => { 118 | // User is not allowed to add any more resources 119 | this.showCreateButton = false 120 | }) 121 | } 122 | }) 123 | ``` 124 | 125 | ### $authorize.isAuthorized() use cases 126 | 127 | ```javascript 128 | // Check if user with guest role has access to resources. 129 | this.$authorize.isAuthorized(['guest']) 130 | ``` 131 | 132 | ```javascript 133 | // Check if user with guest role has permission to access and read resources. 134 | this.$authorize.isAuthorized(['guest'], ['can_read']) 135 | ``` 136 | 137 | ```javascript 138 | // Check if user with any role has permission to edit resources. 139 | this.$authorize.isAuthorized(null, ['can_update']) 140 | ``` 141 | 142 | ### vue-router 143 | 144 | You can easily use this library with [vue-router]() 145 | 146 | Route example: 147 | ```javascript 148 | new VueRouter({ 149 | routes: [ 150 | { 151 | path: '/protected-route', 152 | meta: { 153 | permissions: { 154 | roles: ['user'], 155 | redirectTo: '/login' 156 | } 157 | } 158 | } 159 | ] 160 | }) 161 | ``` 162 | 163 | Once you have setup your route meta data, write simple route change handler to check if user has access to targeted route. 164 | 165 | ```javascript 166 | router.beforeEach(function (to, from, next) { 167 | if (to.meta && to.meta.permissions) { 168 | let roles = to.meta.permissions.roles 169 | let permissions = to.meta.permissions.permissions 170 | 171 | router.app.$authorize.isAuthorized(roles, permissions).then(function () { 172 | next() 173 | }).catch(function () { 174 | next(to.meta.permissions.redirectTo || '/login') 175 | }) 176 | } else { 177 | next() 178 | } 179 | }) 180 | ``` 181 | 182 | 183 | Also, you can use already built-in directive and component to show/hide elements in the template. 184 | 185 | ### Directive (WIP) - need help! 186 | Directive only does show/hide of elements 187 | 188 | ```html 189 |
190 |
191 |
192 | ``` 193 | 194 | ### Component 195 | Component internaly removes elements from the DOM if user does not have access to them. 196 | 197 | ```html 198 | 199 | 200 | 201 | ``` 202 | 203 | 204 | ## License 205 | 206 | The MIT License (MIT) 207 | 208 | Copyright (c) 2017 Davor Grubelić 209 | 210 | Permission is hereby granted, free of charge, to any person obtaining a copy of 211 | this software and associated documentation files (the "Software"), to deal in 212 | the Software without restriction, including without limitation the rights to 213 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 214 | the Software, and to permit persons to whom the Software is furnished to do so, 215 | subject to the following conditions: 216 | 217 | The above copyright notice and this permission notice shall be included in all 218 | copies or substantial portions of the Software. 219 | 220 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 221 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 222 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 223 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 224 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 225 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 226 | -------------------------------------------------------------------------------- /example/acl-config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | roles: { 3 | admin: { 4 | handler: function () { 5 | // return this.$auth.isAuthenticated() 6 | return true 7 | }, 8 | permissions: ['can_read', 'can_create', 'can_update', 'can_moderate', 'can_delete'] 9 | }, 10 | 11 | moderator: { 12 | handler: function () { 13 | return this.$auth.isAuthenticated() 14 | }, 15 | permissions: ['can_moderate'] 16 | }, 17 | 18 | user: { 19 | handler: function () { 20 | return this.$auth.isAuthenticated() 21 | }, 22 | permissions: ['can_read', 'can_create', 'can_update'] 23 | }, 24 | 25 | guest: { 26 | handler: function () { 27 | return !this.$auth.isAuthenticated() 28 | }, 29 | permissions: ['can_read'] 30 | } 31 | }, 32 | 33 | permissions: { 34 | can_read: function () { 35 | return true 36 | }, 37 | 38 | can_create: function () { 39 | return true 40 | }, 41 | 42 | can_update: function () { 43 | return false 44 | }, 45 | 46 | can_moderate: function () { 47 | return true 48 | }, 49 | 50 | can_delete: function () { 51 | return true 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Vue.Authorize 6 | 7 | 92 | 93 | 94 |
95 | 106 | 107 | 108 |
109 | 110 | 111 | -------------------------------------------------------------------------------- /example/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import VueRouter from 'vue-router' 3 | import VueResource from 'vue-resource' 4 | import VueAuthenticate from 'vue-authenticate' 5 | import VueAuthorize from '../src/index.js' 6 | import aclConfig from './acl-config.js' 7 | 8 | Vue.use(VueRouter) 9 | Vue.use(VueResource) 10 | Vue.use(VueAuthenticate) 11 | Vue.use(VueAuthorize, aclConfig) 12 | 13 | const router = new VueRouter({ 14 | mode: 'history', 15 | routes: [ 16 | { 17 | path: '/', 18 | name: 'index', 19 | meta: { 20 | permissions: { 21 | roles: ['user'], 22 | redirectTo: { name: 'login' } 23 | } 24 | }, 25 | component: { 26 | template: ` 27 |
28 |

Index page

29 | 30 |
31 | 32 |
guest
33 |
user:can_create
34 |
*:can_moderate
35 |
36 | ` 37 | } 38 | }, 39 | 40 | { 41 | path: '/profile', 42 | name: 'profile', 43 | meta: { 44 | permissions: { 45 | roles: ['user'], 46 | redirectTo: { name: 'login' } 47 | } 48 | }, 49 | component: { 50 | template: ` 51 |
52 |

Profile page

53 | 54 |
55 | 56 |
guest
57 |
user:can_create
58 |
59 | ` 60 | } 61 | }, 62 | { 63 | path: '/login', 64 | name: 'login', 65 | meta: { 66 | permissions: { 67 | roles: ['guest'], 68 | redirectTo: { name: 'index' } 69 | } 70 | }, 71 | component: { 72 | template: ` 73 |
74 |

Login page

75 | 76 |
77 | 78 | 79 |
80 | `, 81 | methods: { 82 | login() { 83 | this.$auth.setToken({ 84 | token: 'TOKEN_1234567890' 85 | }) 86 | this.$router.push({ name: 'index' }) 87 | } 88 | } 89 | } 90 | }, 91 | { 92 | path: '/logout', 93 | name: 'logout', 94 | meta: { 95 | permissions: { 96 | roles: ['user'], 97 | redirectTo: { name: 'login' } 98 | } 99 | }, 100 | component: { 101 | template: ` 102 |
103 |

Logout page

104 |
`, 105 | created: function () { 106 | this.$auth.logout().then(() => { 107 | this.$router.push({ name: 'login' }) 108 | }) 109 | } 110 | } 111 | } 112 | ] 113 | }) 114 | 115 | router.beforeEach(function (to, from, next) { 116 | if (to.meta && to.meta.permissions) { 117 | let roles = to.meta.permissions.roles 118 | let permissions = to.meta.permissions.permissions 119 | 120 | router.app.$authorize.isAuthorized(roles, permissions).then(function () { 121 | next() 122 | }).catch(function () { 123 | next(to.meta.permissions.redirectTo || { name: 'login' }) 124 | }) 125 | } else { 126 | next() 127 | } 128 | }) 129 | 130 | const app = new Vue({ 131 | router 132 | }).$mount('#app') -------------------------------------------------------------------------------- /example/webpack.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack') 2 | 3 | module.exports = { 4 | entry: __dirname + '/index.js', 5 | output: { 6 | path: __dirname + '/', 7 | filename: 'index.bundle.js' 8 | }, 9 | module: { 10 | rules: [ 11 | { 12 | test: /\.js$/, 13 | exclude: /(node_modules|bower_components)/, 14 | use: { 15 | loader: 'babel-loader', 16 | options: { 17 | // presets: ['es2015'] 18 | } 19 | } 20 | } 21 | ] 22 | }, 23 | resolve: { 24 | alias: { 25 | vue: 'vue/dist/vue.common.js' 26 | } 27 | }, 28 | plugins: [ 29 | // new webpack.optimize.UglifyJsPlugin() 30 | ] 31 | }; -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'), 2 | connect = require('gulp-connect') 3 | webpack = require('webpack'), 4 | gulpWebpack = require('gulp-webpack') 5 | history = require('connect-history-api-fallback') 6 | 7 | gulp.task('compile', function () { 8 | var webpackConfig = require('./example/webpack.config.js') 9 | return gulp.src('./example/index.js') 10 | .pipe(gulpWebpack(webpackConfig, webpack)) 11 | .pipe(gulp.dest('./example')) 12 | .pipe(connect.reload()) 13 | }) 14 | 15 | gulp.task('server', function () { 16 | connect.server({ 17 | name: 'VueAuthorize', 18 | root: './example', 19 | base: 'example', 20 | port: 8080, 21 | livereload: true, 22 | verbose: true, 23 | middleware: function () { 24 | return [history()] 25 | } 26 | }); 27 | }) 28 | 29 | gulp.task('watch', function () { 30 | gulp.watch([ 31 | './example/index.js', 32 | './example/acl-config.js', 33 | './src/**/*.js', 34 | ], ['compile']) 35 | }) 36 | 37 | gulp.task('dev', ['compile', 'server', 'watch']) -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-authorize", 3 | "version": "1.0.0", 4 | "description": "Simple Vue.js authorization library", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "dev": "gulp dev" 8 | }, 9 | "keywords": [ 10 | "vue", 11 | "vuejs", 12 | "auth", 13 | "authorization" 14 | ], 15 | "author": { 16 | "name": "Davor Grubelic", 17 | "email": "davor.grubelic@gmail.com", 18 | "url": "https://dgrubelic.me" 19 | }, 20 | "repository": { 21 | "type": "git", 22 | "url": "https://github.com/dgrubelic/vue-authorize.git" 23 | }, 24 | "license": "MIT", 25 | "dependencies": { 26 | "vue-authenticate": "^1.0.9" 27 | }, 28 | "devDependencies": { 29 | "axios": "^0.15.3", 30 | "babel-core": "^6.24.0", 31 | "babel-loader": "^6.4.1", 32 | "babel-preset-es2015": "^6.24.0", 33 | "body-parser": "^1.17.1", 34 | "connect-history-api-fallback": "^1.3.0", 35 | "cors": "^2.8.1", 36 | "express": "^4.15.2", 37 | "gulp": "^3.9.1", 38 | "gulp-connect": "^5.0.0", 39 | "gulp-webpack": "^1.5.0", 40 | "node-dev": "^3.1.3", 41 | "oauth": "^0.9.15", 42 | "oauth-signature": "^1.3.1", 43 | "parallelshell": "^2.0.0", 44 | "request": "^2.81.0", 45 | "unix-timestamp": "^0.2.0", 46 | "vue-axios": "^2.0.1", 47 | "vue-router": "^2.3.0", 48 | "vuex": "^2.2.1", 49 | "webpack": "^2.3.2", 50 | "webpack-dev-server": "^2.4.2" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/authorize.js: -------------------------------------------------------------------------------- 1 | import Promise from './promise.js' 2 | import { objectExtend, forEach, isString, isFunction, isArray, isObject } from './utils.js' 3 | import defaultOptions from './options.js' 4 | import Role from './authorize/role.js' 5 | import Permission from './authorize/permission.js' 6 | 7 | export default class VueAuthorize { 8 | constructor($auth, overrideOptions) { 9 | let options = objectExtend({}, defaultOptions) 10 | options = objectExtend(options, overrideOptions) 11 | 12 | let roles_ = {} 13 | let permissions_ = {} 14 | 15 | Object.defineProperties(this, { 16 | options: { 17 | get() { 18 | return options 19 | } 20 | }, 21 | 22 | roles: { 23 | get() { 24 | return roles_ 25 | } 26 | }, 27 | 28 | permissions: { 29 | get() { 30 | return permissions_ 31 | } 32 | }, 33 | 34 | $auth: { 35 | get() { 36 | return $auth 37 | } 38 | } 39 | }) 40 | 41 | // First define permissions because roles may include them 42 | forEach(this.options.permissions, (handler, name) => { 43 | this.definePermission(name, handler) 44 | }, this) 45 | 46 | // Once permissions are done, define roles 47 | forEach(this.options.roles, (handler, name) => { 48 | let rolePermissions = [] 49 | if (isObject(handler)) { 50 | this.defineRole(name, handler.handler, handler.permissions) 51 | } else { 52 | this.defineRole(name, handler) 53 | } 54 | }) 55 | } 56 | 57 | defineRole(name, handler, permissions) { 58 | if (this.roles[name]) { 59 | throw new Error(`Role "${name}" already defined`) 60 | } 61 | 62 | if (!isFunction(handler)) { 63 | throw new Error('Role handler must be function') 64 | } 65 | 66 | let rolePermissions = [] 67 | if (isArray(permissions)) { 68 | permissions.map((permissionName) => { 69 | if (this.permissions[permissionName]) { 70 | rolePermissions.push(this.permissions[permissionName]) 71 | } else { 72 | throw new Error(`Unknown permission "${permissionName}"`) 73 | } 74 | }) 75 | } 76 | 77 | this.roles[name] = new Role(name, handler, rolePermissions) 78 | } 79 | 80 | definePermission(name, handler) { 81 | if (this.permissions[name]) { 82 | throw new Error(`Permission "${name}" already defined`) 83 | } 84 | 85 | this.permissions[name] = new Permission(name, handler) 86 | } 87 | 88 | /** 89 | * Check user authorization status 90 | * @param {String|Array} role 91 | * @param {String|Array} permission 92 | * @return {Boolean|Promise} 93 | */ 94 | isAuthorized(role, permission) { 95 | let roleInstances = [] 96 | if (role) { 97 | if (!isArray(role)) { 98 | role = [role] 99 | } 100 | 101 | forEach(this.roles, (roleInstance, roleName) => { 102 | if (role.indexOf(roleInstance.name) >= 0) { 103 | roleInstances.push(roleInstance) 104 | } 105 | }) 106 | } else { 107 | forEach(this.roles, (roleInstance) => { 108 | roleInstances.push(roleInstance) 109 | }) 110 | } 111 | 112 | if (permission && !isArray(permission)) { 113 | permission = [permission] 114 | } 115 | 116 | return new Promise((resolve, reject) => { 117 | if (roleInstances.length === 0) { 118 | reject() 119 | } 120 | 121 | return Promise.any(roleInstances.map((roleInstance) => { 122 | return roleInstance.invoke(permission, this) 123 | })).then(resolve).catch(reject) 124 | }) 125 | } 126 | } -------------------------------------------------------------------------------- /src/authorize/permission.js: -------------------------------------------------------------------------------- 1 | import { isFunction, isPromise } from '../utils.js' 2 | import Promise from '../promise.js' 3 | 4 | export default class Permission { 5 | constructor(name, handler) { 6 | this.name = name 7 | 8 | if (isFunction(handler)) { 9 | this.handler = handler 10 | } else { 11 | throw new Error('Permission handler must be function.') 12 | } 13 | } 14 | 15 | /** 16 | * Invoke permission handler 17 | * @param {Object} context Permission handler context 18 | * @return {Promise} 19 | */ 20 | invoke(context) { 21 | let invokeResult = this.handler.call(context || this) 22 | if (isPromise(invokeResult)) { 23 | return invokeResult 24 | } else { 25 | return new Promise(function (resolve, reject) { 26 | if (!!invokeResult) { 27 | resolve() 28 | } else { 29 | reject() 30 | } 31 | }) 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /src/authorize/role.js: -------------------------------------------------------------------------------- 1 | import Promise from '../promise.js' 2 | import { isArray, isPromise } from '../utils.js' 3 | 4 | export default class Role { 5 | constructor(name, handler, permissions) { 6 | this.name = name 7 | this.handler = handler 8 | this.permissions = permissions 9 | } 10 | 11 | invoke(permission, context) { 12 | if (!context) { 13 | context = permission 14 | permission = null 15 | } 16 | 17 | if (permission && !isArray(permission)) { 18 | permission = [permission] 19 | } 20 | 21 | let permissionInstances = null; 22 | if (permission) { 23 | permissionInstances = this.permissions.filter((permissionInstance) => { 24 | return (permission.indexOf(permissionInstance.name) >= 0) 25 | }) 26 | if (permissionInstances.length === 0) { 27 | permissionInstances = null 28 | } 29 | } 30 | 31 | let invokeResult = this.handler.call(context || this) 32 | if (isPromise(invokeResult)) { 33 | if (permission) { 34 | if (permissionInstances) { 35 | // Permissions passed to check, run their invocation 36 | return invokeResult.then(() => { 37 | return Promise.any(permissionInstances.map((permissionInstance) => { 38 | return permissionInstance.invoke(context || this) 39 | })).then(() => { 40 | return Promise.resolve() 41 | }, () => { 42 | return Promise.reject() 43 | }) 44 | }) 45 | } else { 46 | Promise.reject() 47 | } 48 | } else { 49 | // No permissions passed, return invoked result 50 | return invokeResult 51 | } 52 | } else { 53 | return new Promise(function (resolve, reject) { 54 | if (!!invokeResult) { 55 | if (permission) { 56 | if (permissionInstances) { 57 | return Promise.any(permissionInstances.map((permissionInstance) => { 58 | return permissionInstance.invoke(context || this) 59 | })).then(resolve, reject) 60 | } else { 61 | reject() 62 | } 63 | } else { 64 | resolve() 65 | } 66 | } else { 67 | reject() 68 | } 69 | }) 70 | } 71 | } 72 | } -------------------------------------------------------------------------------- /src/component.js: -------------------------------------------------------------------------------- 1 | export default { 2 | template: ` 3 |
4 | 5 |
6 | `, 7 | 8 | props: ['roles', 'permissions'], 9 | 10 | data() { 11 | return { 12 | isVisible: false 13 | } 14 | }, 15 | 16 | created () { 17 | this.authorize() 18 | }, 19 | 20 | updated () { 21 | this.authorize() 22 | }, 23 | 24 | methods: { 25 | authorize() { 26 | this.$authorize.isAuthorized(this.roles, this.permissions).then(() => { 27 | this.isVisible = true 28 | }).catch(() => { 29 | this.isVisible = false 30 | }) 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/directive.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | /** 4 | * This directive should serve as a way to 5 | * remove DOM elements if user is not auhtorized 6 | * to see or interact with them. 7 | * 8 | * Example: 9 | * 10 | * Current user role: guest 11 | * 12 | *
13 | *
14 | * 15 | * 16 | * 17 | * Other examples: 18 | * 19 | * Current user role: user 20 | * Current user permissions: ['can_read', can_create', 'can_update'] 21 | * 22 | *
24 | *
25 | * 26 | */ 27 | export default { 28 | bind: function (el, bindings, vnode) { 29 | el.style.display = 'none' 30 | compileAuthorize(el, bindings, vnode) 31 | }, 32 | 33 | update: function (el, bindings, vnode) { 34 | compileAuthorize(el, bindings, vnode) 35 | }, 36 | 37 | componentUpdated: function (el, bindings, vnode) { 38 | compileAuthorize(el, bindings, vnode) 39 | }, 40 | 41 | unbind: function (el) { 42 | el.style.display = null 43 | } 44 | } 45 | 46 | function compileAuthorize(el, bindings, vnode) { 47 | let roles = bindings.value.roles || null 48 | let permissions = bindings.value.permissions || null 49 | 50 | // Run authorization 51 | vnode.context.$authorize.isAuthorized(roles, permissions).then(function () { 52 | el.style.display = null 53 | }).catch(function () { 54 | el.style.display = 'none' 55 | }) 56 | } 57 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import './utils.js' 2 | import { objectExtend } from './utils.js' 3 | import VueAuthorize from './authorize.js' 4 | import IsAuthorizedDirective from './directive.js' 5 | import IsAuthorizedComponent from './component.js' 6 | 7 | /** 8 | * VueAuthorize plugin 9 | * @param {Object} Vue 10 | */ 11 | function plugin(Vue, options) { 12 | if (plugin.installed) { 13 | return 14 | } 15 | 16 | Vue.directive('isAuthorized', IsAuthorizedDirective) 17 | Vue.component('isAuthorized', IsAuthorizedComponent) 18 | 19 | let vueAuthorizeInstance = null 20 | Object.defineProperties(Vue.prototype, { 21 | $authorize: { 22 | get() { 23 | if (!vueAuthorizeInstance) { 24 | // vue-authenticate module not found, throw error 25 | if (!this.$auth) { 26 | throw new Error('Missing "vue-authenticate" library') 27 | } 28 | vueAuthorizeInstance = new VueAuthorize(this.$auth, options) 29 | } 30 | return vueAuthorizeInstance 31 | } 32 | } 33 | }) 34 | } 35 | 36 | export default plugin 37 | export { VueAuthorize } -------------------------------------------------------------------------------- /src/options.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 3 | } -------------------------------------------------------------------------------- /src/promise.js: -------------------------------------------------------------------------------- 1 | import Promise from 'promise-polyfill' 2 | 3 | Promise.any = function(arrayOfPromises) { 4 | if(!arrayOfPromises || !(arrayOfPromises instanceof Array)) { 5 | throw new Error('Must pass Promise.any an array') 6 | } 7 | 8 | if(arrayOfPromises.length === 0) { 9 | return Promise.resolve([]) 10 | } 11 | 12 | 13 | // For each promise that resolves or rejects, 14 | // make them all resolve. 15 | // Record which ones did resolve or reject 16 | var resolvingPromises = arrayOfPromises.map(function(promise) { 17 | return promise.then(function(result) { 18 | return { 19 | resolve: true, 20 | result: result 21 | } 22 | }, function(error) { 23 | return { 24 | resolve: false, 25 | result: error 26 | } 27 | }) 28 | }) 29 | 30 | return Promise.all(resolvingPromises).then(function(results) { 31 | // Count how many passed/failed 32 | var passed = [], failed = [], allFailed = true 33 | results.forEach(function(result) { 34 | if(result.resolve) { 35 | allFailed = false 36 | } 37 | passed.push(result.resolve ? result.result : null) 38 | failed.push(result.resolve ? null : result.result) 39 | }); 40 | 41 | if(allFailed) { 42 | throw failed 43 | } else { 44 | return passed 45 | } 46 | }) 47 | } 48 | 49 | export default Promise -------------------------------------------------------------------------------- /src/utils.js: -------------------------------------------------------------------------------- 1 | if (typeof Object.assign != 'function') { 2 | Object.assign = function(target, varArgs) { 3 | 'use strict'; 4 | if (target == null) { 5 | throw new TypeError('Cannot convert undefined or null to object'); 6 | } 7 | 8 | var to = Object(target); 9 | 10 | for (var index = 1; index < arguments.length; index++) { 11 | var nextSource = arguments[index]; 12 | 13 | if (nextSource != null) { // Skip over if undefined or null 14 | for (var nextKey in nextSource) { 15 | // Avoid bugs when hasOwnProperty is shadowed 16 | if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { 17 | to[nextKey] = nextSource[nextKey]; 18 | } 19 | } 20 | } 21 | } 22 | return to; 23 | }; 24 | } 25 | 26 | export function camelCase(name) { 27 | return name.replace(/([\:\-\_]+(.))/g, function (_, separator, letter, offset) { 28 | return offset ? letter.toUpperCase() : letter; 29 | }); 30 | } 31 | 32 | export function isUndefined(value) { 33 | return typeof value === 'undefined' 34 | } 35 | 36 | export function isDefined(value) { 37 | return typeof value !== 'undefined' 38 | } 39 | 40 | export function isObject(value) { 41 | return value !== null && typeof value === 'object' 42 | } 43 | 44 | export function isBlankObject(value) { 45 | return value !== null && typeof value === 'object' && !getPrototypeOf(value); 46 | } 47 | 48 | export function isArray(value) { 49 | return Array.isArray(value) 50 | } 51 | 52 | function isArrayLike(obj) { 53 | if (obj == null || isWindow(obj)) return false; 54 | if (isArray(obj) || isString(obj)) return true; 55 | var length = 'length' in Object(obj) && obj.length; 56 | return isNumber(length) && 57 | (length >= 0 && ((length - 1) in obj || obj instanceof Array) || typeof obj.item === 'function'); 58 | 59 | } 60 | 61 | export function isString(value) { 62 | return typeof value === 'string' 63 | } 64 | 65 | export function isNumber(value) { 66 | return typeof value === 'number' 67 | } 68 | 69 | export function isFunction(value) { 70 | return typeof value === 'function' 71 | } 72 | 73 | export function isPromise(value) { 74 | return value && isFunction(value.then) 75 | } 76 | 77 | export function isWindow(value) { 78 | return value && value.window === value; 79 | } 80 | 81 | export function getPrototypeOf(value) { 82 | return Object.getPrototypeOf(value) 83 | } 84 | 85 | export function objectExtend(a, b) { 86 | 87 | // Don't touch 'null' or 'undefined' objects. 88 | if (a == null || b == null) { 89 | return a; 90 | } 91 | 92 | Object.keys(b).forEach(function (key) { 93 | if (Object.prototype.toString.call(b[key]) == '[object Object]') { 94 | if (Object.prototype.toString.call(a[key]) != '[object Object]') { 95 | a[key] = b[key]; 96 | } else { 97 | a[key] = objectExtend(a[key], b[key]); 98 | } 99 | } else { 100 | a[key] = b[key]; 101 | } 102 | }); 103 | 104 | return a; 105 | }; 106 | 107 | export function forEach(obj, iterator, context) { 108 | var key, length; 109 | if (obj) { 110 | if (isFunction(obj)) { 111 | for (key in obj) { 112 | if (key !== 'prototype' && key !== 'length' && key !== 'name' && obj.hasOwnProperty(key)) { 113 | iterator.call(context, obj[key], key, obj); 114 | } 115 | } 116 | } else if (isArray(obj) || isArrayLike(obj)) { 117 | var isPrimitive = typeof obj !== 'object'; 118 | for (key = 0, length = obj.length; key < length; key++) { 119 | if (isPrimitive || key in obj) { 120 | iterator.call(context, obj[key], key, obj); 121 | } 122 | } 123 | } else if (obj.forEach && obj.forEach !== forEach) { 124 | obj.forEach(iterator, context, obj); 125 | } else if (isBlankObject(obj)) { 126 | // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty 127 | for (key in obj) { 128 | iterator.call(context, obj[key], key, obj); 129 | } 130 | } else if (typeof obj.hasOwnProperty === 'function') { 131 | // Slow path for objects inheriting Object.prototype, hasOwnProperty check needed 132 | for (key in obj) { 133 | if (obj.hasOwnProperty(key)) { 134 | iterator.call(context, obj[key], key, obj); 135 | } 136 | } 137 | } else { 138 | // Slow path for objects which do not have a method `hasOwnProperty` 139 | for (key in obj) { 140 | if (hasOwnProperty.call(obj, key)) { 141 | iterator.call(context, obj[key], key, obj); 142 | } 143 | } 144 | } 145 | } 146 | return obj; 147 | } 148 | 149 | /** 150 | * Assemble url from two segments 151 | * 152 | * @author Sahat Yalkabov 153 | * @copyright Method taken from https://github.com/sahat/satellizer 154 | * 155 | * @param {String} baseUrl Base url 156 | * @param {String} url URI 157 | * @return {String} 158 | */ 159 | export function joinUrl(baseUrl, url) { 160 | if (/^(?:[a-z]+:)?\/\//i.test(url)) { 161 | return url; 162 | } 163 | let joined = [baseUrl, url].join('/'); 164 | let normalize = function (str) { 165 | return str 166 | .replace(/[\/]+/g, '/') 167 | .replace(/\/\?/g, '?') 168 | .replace(/\/\#/g, '#') 169 | .replace(/\:\//g, '://'); 170 | }; 171 | return normalize(joined); 172 | } 173 | 174 | /** 175 | * Get full path based on current location 176 | * 177 | * @author Sahat Yalkabov 178 | * @copyright Method taken from https://github.com/sahat/satellizer 179 | * 180 | * @param {Location} location 181 | * @return {String} 182 | */ 183 | export function getFullUrlPath(location) { 184 | const isHttps = location.protocol === 'https:'; 185 | return location.protocol + '//' + location.hostname + 186 | ':' + (location.port || (isHttps ? '443' : '80')) + 187 | (/^\//.test(location.pathname) ? location.pathname : '/' + location.pathname); 188 | } 189 | 190 | /** 191 | * Parse query string variables 192 | * 193 | * @author Sahat Yalkabov 194 | * @copyright Method taken from https://github.com/sahat/satellizer 195 | * 196 | * @param {String} Query string 197 | * @return {String} 198 | */ 199 | export function parseQueryString(str) { 200 | let obj = {}; 201 | let key; 202 | let value; 203 | (str || '').split('&').forEach((keyValue) => { 204 | if (keyValue) { 205 | value = keyValue.split('='); 206 | key = decodeURIComponent(value[0]); 207 | obj[key] = (!!value[1]) ? decodeURIComponent(value[1]) : true; 208 | } 209 | }); 210 | return obj; 211 | } 212 | 213 | /** 214 | * Decode base64 string 215 | * @author Sahat Yalkabov 216 | * @copyright Method taken from https://github.com/sahat/satellizer 217 | * 218 | * @param {String} str base64 encoded string 219 | * @return {Object} 220 | */ 221 | export function decodeBase64(str) { 222 | let buffer; 223 | if (typeof module !== 'undefined' && module.exports) { 224 | try { 225 | buffer = require('buffer').Buffer; 226 | } catch (err) { 227 | // noop 228 | } 229 | } 230 | 231 | let fromCharCode = String.fromCharCode; 232 | 233 | let re_btou = new RegExp([ 234 | '[\xC0-\xDF][\x80-\xBF]', 235 | '[\xE0-\xEF][\x80-\xBF]{2}', 236 | '[\xF0-\xF7][\x80-\xBF]{3}' 237 | ].join('|'), 'g'); 238 | 239 | let cb_btou = function (cccc) { 240 | switch (cccc.length) { 241 | case 4: 242 | let cp = ((0x07 & cccc.charCodeAt(0)) << 18) 243 | | ((0x3f & cccc.charCodeAt(1)) << 12) 244 | | ((0x3f & cccc.charCodeAt(2)) << 6) 245 | | (0x3f & cccc.charCodeAt(3)); 246 | let offset = cp - 0x10000; 247 | return (fromCharCode((offset >>> 10) + 0xD800) 248 | + fromCharCode((offset & 0x3FF) + 0xDC00)); 249 | case 3: 250 | return fromCharCode( 251 | ((0x0f & cccc.charCodeAt(0)) << 12) 252 | | ((0x3f & cccc.charCodeAt(1)) << 6) 253 | | (0x3f & cccc.charCodeAt(2)) 254 | ); 255 | default: 256 | return fromCharCode( 257 | ((0x1f & cccc.charCodeAt(0)) << 6) 258 | | (0x3f & cccc.charCodeAt(1)) 259 | ); 260 | } 261 | }; 262 | 263 | let btou = function (b) { 264 | return b.replace(re_btou, cb_btou); 265 | }; 266 | 267 | let _decode = buffer ? function (a) { 268 | return (a.constructor === buffer.constructor 269 | ? a : new buffer(a, 'base64')).toString(); 270 | } 271 | : function (a) { 272 | return btou(atob(a)); 273 | }; 274 | 275 | return _decode( 276 | String(str).replace(/[-_]/g, function (m0) { 277 | return m0 === '-' ? '+' : '/'; 278 | }) 279 | .replace(/[^A-Za-z0-9\+\/]/g, '') 280 | ); 281 | } --------------------------------------------------------------------------------