├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── lib ├── default.js ├── logic.js ├── mysql.js └── rbac.js ├── package.json └── test ├── README.md ├── app.js ├── bin └── www ├── database ├── config.js └── mysql.js ├── package.json ├── public ├── assets │ ├── css │ │ ├── 4095-rtl.min.css │ │ ├── animate.min.css │ │ ├── beyond-rtl.min.css │ │ ├── beyond.min.css │ │ ├── bootstrap-rtl.min.css │ │ ├── bootstrap.min.css │ │ ├── dataTables.bootstrap.css │ │ ├── demo.min.css │ │ ├── font-awesome.min.css │ │ ├── skins │ │ │ ├── azure.min.css │ │ │ ├── black.min.css │ │ │ ├── blue.min.css │ │ │ ├── darkblue.min.css │ │ │ ├── darkred.min.css │ │ │ ├── deepblue.min.css │ │ │ ├── gray.min.css │ │ │ ├── green.min.css │ │ │ ├── orange.min.css │ │ │ ├── pink.min.css │ │ │ ├── purple.min.css │ │ │ └── teal.min.css │ │ ├── typicons.min.css │ │ └── weather-icons.min.css │ ├── fonts │ │ ├── fontawesome-webfont.eot │ │ ├── fontawesome-webfont.svg │ │ ├── fontawesome-webfont.ttf │ │ ├── fontawesome-webfont.woff │ │ ├── glyphicons-halflings-regular.eot │ │ ├── glyphicons-halflings-regular.svg │ │ ├── glyphicons-halflings-regular.ttf │ │ ├── glyphicons-halflings-regular.woff │ │ ├── typicons.eot │ │ ├── typicons.svg │ │ ├── typicons.ttf │ │ ├── typicons.woff │ │ ├── weathericons-regular-webfont.eot │ │ ├── weathericons-regular-webfont.svg │ │ ├── weathericons-regular-webfont.ttf │ │ └── weathericons-regular-webfont.woff │ ├── img │ │ ├── attach-blue.png │ │ ├── attach-green.png │ │ ├── attach-red.png │ │ ├── attach-yellow.png │ │ ├── avatars │ │ │ ├── Javi-Jimenez.jpg │ │ │ ├── John-Smith.jpg │ │ │ ├── Matt-Cheuvront.jpg │ │ │ ├── Nicolai-Larson.jpg │ │ │ ├── Osvaldus-Valutis.jpg │ │ │ ├── Sergey-Azovskiy.jpg │ │ │ ├── Stephanie-Walter.jpg │ │ │ ├── adam-jansen.jpg │ │ │ ├── bing.png │ │ │ └── divyia.jpg │ │ ├── favicon.png │ │ ├── jquery.minicolors.png │ │ ├── logo-inverted.png │ │ ├── logo-rtl.png │ │ ├── logo.png │ │ ├── sort_asc.png │ │ ├── sort_asc_disabled.png │ │ ├── sort_both.png │ │ ├── sort_desc.png │ │ ├── sort_desc_disabled.png │ │ └── temp1.png │ └── js │ │ ├── beyond.js │ │ ├── beyond.min.js │ │ ├── bootbox │ │ └── bootbox.js │ │ ├── bootstrap.min.js │ │ ├── charts │ │ ├── chartjs │ │ │ ├── Chart.js │ │ │ └── chartjs-init.js │ │ ├── easypiechart │ │ │ ├── easypiechart-init.js │ │ │ └── jquery.easypiechart.js │ │ ├── flot │ │ │ ├── flot-init.js │ │ │ ├── jquery.flot.crosshair.js │ │ │ ├── jquery.flot.js │ │ │ ├── jquery.flot.orderBars.js │ │ │ ├── jquery.flot.pie.js │ │ │ ├── jquery.flot.resize.js │ │ │ ├── jquery.flot.selection.js │ │ │ ├── jquery.flot.stack.js │ │ │ ├── jquery.flot.time.js │ │ │ └── jquery.flot.tooltip.js │ │ ├── morris │ │ │ ├── morris-init.js │ │ │ ├── morris.js │ │ │ └── raphael-2.0.2.min.js │ │ └── sparkline │ │ │ ├── jquery.sparkline.js │ │ │ └── sparkline-init.js │ │ ├── colorpicker │ │ └── jquery.minicolors.js │ │ ├── datatable │ │ ├── ZeroClipboard.js │ │ ├── assets │ │ │ └── swf │ │ │ │ └── copy_csv_xls_pdf.swf │ │ ├── dataTables.bootstrap.min.js │ │ ├── dataTables.tableTools.min.js │ │ ├── datatables-init.js │ │ └── jquery.dataTables.min.js │ │ ├── datetime │ │ ├── bootstrap-datepicker.js │ │ ├── bootstrap-timepicker.js │ │ ├── daterangepicker.js │ │ └── moment.js │ │ ├── editors │ │ ├── summernote │ │ │ └── summernote.js │ │ └── wysiwyg │ │ │ ├── bootstrap-wysiwyg.js │ │ │ ├── jquery.hotkeys.js │ │ │ └── prettify.js │ │ ├── fuelux │ │ ├── spinner │ │ │ └── fuelux.spinner.min.js │ │ ├── treeview │ │ │ ├── tree-custom.min.js │ │ │ └── treeview-init.js │ │ └── wizard │ │ │ └── wizard-custom.js │ │ ├── fullcalendar │ │ └── fullcalendar.js │ │ ├── jquery-2.0.3.min.js │ │ ├── jquery-ui-1.10.4.custom.js │ │ ├── knob │ │ └── jquery.knob.js │ │ ├── nestable │ │ └── jquery.nestable.min.js │ │ ├── select2 │ │ └── select2.js │ │ ├── skins.min.js │ │ ├── slider │ │ ├── jQRangeSlider │ │ │ └── jQAllRangeSliders-withRuler-min.js │ │ └── jquery.nouislider.js │ │ ├── swf │ │ └── copy_csv_xls_pdf.swf │ │ ├── tagsinput │ │ └── bootstrap-tagsinput.js │ │ ├── textarea │ │ └── jquery.autosize.js │ │ ├── toastr │ │ └── toastr.js │ │ └── validation │ │ └── bootstrapValidator.js ├── images │ ├── cover.png │ └── logo.jpg └── module │ ├── bootstrap-3.3.7 │ ├── css │ │ ├── bootstrap-theme.css │ │ ├── bootstrap-theme.css.map │ │ ├── bootstrap-theme.min.css │ │ ├── bootstrap-theme.min.css.map │ │ ├── bootstrap.css │ │ ├── bootstrap.css.map │ │ ├── bootstrap.min.css │ │ └── bootstrap.min.css.map │ ├── fonts │ │ ├── glyphicons-halflings-regular.eot │ │ ├── glyphicons-halflings-regular.svg │ │ ├── glyphicons-halflings-regular.ttf │ │ ├── glyphicons-halflings-regular.woff │ │ └── glyphicons-halflings-regular.woff2 │ └── js │ │ ├── bootstrap.js │ │ ├── bootstrap.min.js │ │ └── npm.js │ └── jQuery │ └── jquery.js ├── rbac.sql ├── routes ├── admin.js ├── index.js ├── logic │ └── fn.js └── users.js └── views ├── admin ├── index.pug ├── lib │ ├── info.pug │ ├── javascripts.pug │ ├── layout.pug │ ├── navbar.pug │ ├── readme │ ├── sidebar.pug │ └── styleJs.pug ├── purview │ ├── add_node.pug │ ├── add_node_tag.pug │ ├── add_role.pug │ ├── add_user.pug │ ├── node_list.pug │ ├── node_tag.pug │ ├── readme │ ├── role_list.pug │ ├── role_node_edit.pug │ └── user_list.pug └── readme ├── error.pug ├── material ├── content.pug ├── index.pug ├── lib │ ├── header.pug │ ├── layout.pug │ ├── mixins.pug │ ├── readme │ └── styleJS.pug ├── login.pug └── readme └── module ├── mixins.pug └── readme /.gitignore: -------------------------------------------------------------------------------- 1 | # Node Files # 2 | node_modules 3 | npm-debug.log 4 | 5 | package-lock.json -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # Node Files # 2 | node_modules 3 | npm-debug.log 4 | 5 | package-lock.json 6 | 7 | test/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 版权所有(C)2012-2016 由贡献者邓登攀拥有 2 | 3 | 特此授予任何获得副本的人免费的免费许可 4 | 的这个软件和相关的文档文件(“软件”),来处理 5 | 在软件中没有限制,包括但不限于权利 6 | 使用,复制,修改,合并,发布,分发,再许可和/或销售 7 | 软件副本,以及允许软件所属的人员 8 | 在符合以下条件的情况下: 9 | 10 | 以上版权声明和本许可声明应包含在 11 | 软件的所有副本或重要部分。 12 | 13 | 本软件“按原样”提供,不提供任何种类的明示或明示担保 14 | 暗示,包括但不限于适销性的保证, 15 | 适用于特定用途和非侵权。在任何情况下 16 | 作者或版权持有者对任何索赔,损害或其他责任 17 | 责任,无论在合同,侵权或其他方面的行为, 18 | 超出或与软件或使用或其他交易有关 19 | 软件。 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mysql_rbac 2 | 3 | 一个基于node+mysql+express的rbac权限管理模块 4 | 5 | 6 | ### 依赖模块. 7 | - express 8 | - express-session 9 | - mysql 10 | 11 | 12 | ### 快速安装 13 | 14 | mysql_rbac 需要 [Node.js](https://nodejs.org/) v4+ 才能运行. 15 | 16 | 17 | ```sh 18 | 19 | $ cd 你的项目路径 20 | 21 | $ npm install mysql_rbac --save 22 | 23 | ``` 24 | 25 | ### 快速上手 26 | 27 | #### 1、在数据库创建必要表及字段 28 | 29 | 用户表(可自定义表名) 30 | 31 | ```sh 32 | 33 | CREATE TABLE `user` ( 34 | `id` int(11) unsigned NOT NULL AUTO_INCREMENT, 35 | `username` char(20) DEFAULT '', 36 | `password` char(32) DEFAULT '', 37 | `logintime` int(10) unsigned DEFAULT '0', 38 | `loginip` varchar(30) DEFAULT '', 39 | `loginlock` tinyint(1) unsigned DEFAULT '0', 40 | `role_id` text COMMENT '所属角色_id *必要', 41 | PRIMARY KEY (`id`) 42 | ) ENGINE=MyISAM AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COMMENT='用户表'; 43 | 44 | 注意:所属角色id 是储存角色表ID的值 并以 " 1,2,3 " 形式储存 (所属角色字段名可自定义) 45 | 46 | ``` 47 | 角色表(可自定义表名) 48 | 49 | ```sh 50 | 51 | CREATE TABLE `role` ( 52 | `id` int(5) unsigned NOT NULL AUTO_INCREMENT, 53 | `name` varchar(20) NOT NULL, 54 | `remark` varchar(255) DEFAULT NULL, 55 | `node_id` text COMMENT '所属权限id *必要', 56 | PRIMARY KEY (`id`) 57 | ) ENGINE=MyISAM AUTO_INCREMENT=5 DEFAULT CHARSET=utf8 COMMENT='角色表'; 58 | 59 | 注意:所属权限id 是储存权限表ID的值 并以 " 1,2,3 " 形式储存 (所属权限字段名可自定义) 60 | 61 | ``` 62 | 权限表(可自定义表名) 63 | 64 | ```sh 65 | 66 | CREATE TABLE `node` ( 67 | `id` int(6) unsigned NOT NULL AUTO_INCREMENT, 68 | `name` varchar(20) NOT NULL, 69 | `route` text COMMENT '所属路由 *必要', 70 | `method` varchar(10) COMMENT '所属路由方法 GET POST GET/POST *必要', 71 | `tag` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '权限分类', 72 | PRIMARY KEY (`id`), 73 | KEY `name` (`name`) 74 | ) ENGINE=MyISAM AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COMMENT='权限表'; 75 | 76 | 注意:所属路由 是储存URL的值 并以 "admin/路由1,路由2/admin, user/路由3/edit" 形式储存 (所属路由字段名可自定义) 77 | 78 | ``` 79 | 80 | #### 2,引入mysql_rbac模块并初始化参数 81 | 82 | 83 | ```sh 84 | 85 | var rbac = require('mysql_rbac'); 86 | 87 | app.use(rbac({ 88 | rbac: { 89 | 'rbac_type': 1, //验证类型(1:登录验证, 2:时时验证) *可选 默认为1 90 | 'user_key': 'uid', //用户认证识别号 *可选 默认为uid 91 | 'role': 'role', //角色表名称 *可选 默认为role 92 | 'node': 'node', //权限表名称 *可选 默认为node 93 | 'user': 'user', //用户表名称 *可选 默认为user 94 | 'userTrole': 'role_id', //在用户表储存角色表id的字段 *可选 默认为role_id 95 | 'roleTnode': 'node_id', //在角色表储存权限表id的字段 *可选 默认为node_id 96 | 'nodeTroute': 'route', //在权限表储存判断权限路由的字段 *可选 默认为route 97 | 'nodeTmethod': 'method', //在权限表储存判断权限路由方法的字段 *可选 默认为method , 98 | 'userName': 'username', //用户表的用户名字段 *可选 默认为username , 99 | 'superUser': 'admin' //用户表的用户名,该用户拥有最高权限 *可选 默认为空 100 | }, 101 | mysql: { 102 | 'host': '127.0.0.1', //IP/域名 *可选 默认为127.0.0.1 103 | 'user': 'root', //数据库账号 *可选 默认为root 104 | 'password': 'root', //数据库密码 *可选 默认为root 105 | 'database': 'mysql_rbac', //数据库库名 *可选 默认为mysq_rbac 106 | 'port': 3306 // 端口 *可选 默认为3306 107 | }, 108 | hook: { 109 | // 在req参数挂载权限状态 如: req.is_root *可选 默认为is_root 110 | root: 'is_root', 111 | // 在session参数挂载权限, 区分是否(1:登录验证, 2:时时验证) 状态 如: req.session.rbac_route *可选 默认为rbac_route 112 | rbac_route: 'rbac_route' 113 | } 114 | })); 115 | 116 | 注意:要在express-session配置参数之后初始化 117 | 118 | 如: 119 | var http = require('http'); 120 | var express = require('express'); 121 | var session = require('express-session'); 122 | var rbac = require('mysql_rbac'); 123 | var app = express(); 124 | 125 | app.use(session({ 126 | secret: 'mysql_rbac', 127 | cookie: { maxAge: 60 * 60 * 24 * 1000 }, 128 | name: 'NODESESSID' 129 | })); 130 | 131 | 132 | app.use(rbac({ 133 | // 参数 134 | })); 135 | 136 | 以下挂载你的路由 137 | app.use('/', index); 138 | router.get('/rolelist', function(req, res, next) { 139 | 140 | req.is_root(function(status){ 141 | if( !status ){ 142 | 143 | return res.end('没有权限!'); 144 | } 145 | 146 | res.end('正常逻辑业务'); 147 | 148 | 149 | }); 150 | }); 151 | http.createServer(app).listen('3000', function() { 152 | console.log(`NodePress Run!port at 3000`) 153 | }); 154 | ``` 155 | 156 | #### 3,使用 157 | 158 | 159 | ```sh 160 | 161 | router.get('/rolelist', function(req, res, next) { 162 | 163 | req.is_root(function(status){ 164 | if( !status ){ 165 | 166 | return res.end('没有权限!'); 167 | } 168 | 169 | 170 | 171 | 172 | }); 173 | }); 174 | 175 | ``` 176 | 177 | 或者 178 | 179 | 180 | ```sh 181 | 182 | router.get('/rolelist', function(req, res, next) { 183 | 184 | req.is_root(function(status){ 185 | if( !status ){ 186 | 187 | return res.end('没有权限!'); 188 | } 189 | 190 | next(); 191 | }); 192 | }, function(req, res, next) { 193 | 194 | 195 | }); 196 | }); 197 | 198 | ``` 199 | 200 | 或者 201 | 202 | 203 | ```sh 204 | 205 | var is_root = function(req, res, next) { 206 | 207 | req.is_root(function(status){ 208 | if( !status ){ 209 | 210 | return res.end('没有权限!'); 211 | } 212 | 213 | next(); 214 | }); 215 | } 216 | 217 | router.get('/rolelist', is_root, function(req, res, next) { 218 | 219 | 220 | }); 221 | }); 222 | 223 | ``` 224 | 225 | ### 日志 226 | 227 | 2018-04-02 增加路由表对method的支持,并增加最高权限的字段分配,分别对应`nodeTmethod`,`userName`, `superUser`三个参数 228 | 229 | ### 许可证 230 | 231 | MIT 232 | 233 | ### 支持 234 | 235 | 如果觉得对自己有用,记得在[https://github.com/pandashuai/mysql_rbac](https://github.com/pandashuai/mysql_rbac) 点 star 支持一下,你的支持是本人的无限动力! 236 | -------------------------------------------------------------------------------- /lib/default.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | rbac: { 3 | 'rbac_type': 1, //验证类型(1:登录验证, 2:时时验证) *可选 默认为1 4 | 'user_key': 'uid', //用户认证识别号 *可选 默认为uid 5 | 'role': 'role', //角色表名称 *可选 默认为role 6 | 'node': 'node', //权限表名称 *可选 默认为node 7 | 'user': 'user', //用户表名称 *可选 默认为user 8 | 'userTrole': 'role_id', //在用户表储存角色表id的字段 *可选 默认为role_id 9 | 'roleTnode': 'node_id', //在角色表储存权限表id的字段 *可选 默认为node_id 10 | 'nodeTroute': 'route', //在权限表储存判断权限路由的字段 *可选 默认为route 11 | 'nodeTmethod': 'method', //在权限表储存判断权限路由方法的字段 *可选 默认为method , 12 | 'userName': 'username', //用户表的用户名字段 *可选 默认为username , 13 | 'superUser': '' //用户表的用户名,该用户拥有最高权限 *可选 默认为空 14 | }, 15 | mysql: { 16 | 'host': '127.0.0.1', //IP/域名 *可选 默认为127.0.0.1 17 | 'user': 'root', //数据库账号 *可选 默认为root 18 | 'password': 'root', //数据库密码 *可选 默认为root 19 | 'database': 'mysql_rbac', //数据库库名 *可选 默认为mysq_rbac 20 | 'port': 3306 // 端口 *可选 默认为3306 21 | }, 22 | hook: { 23 | // 在req参数挂载权限状态 如: req.is_root *可选 默认为is_root 24 | root: 'is_root', 25 | // 在session参数挂载权限, 区分是否(1:登录验证, 2:时时验证) 状态 如: req.session.rbac_route *可选 默认为rbac_route 26 | rbac_route: 'rbac_route' 27 | } 28 | } -------------------------------------------------------------------------------- /lib/logic.js: -------------------------------------------------------------------------------- 1 | // 数组去重 2 | var unique = function(arr) { 3 | var res = []; 4 | var json = {}; 5 | for (var i = 0, el; 6 | (el = arr[i]), el != null; i++) { 7 | if (arr[i] && !json[arr[i]]) { 8 | res.push(arr[i]); 9 | json[arr[i]] = 1; 10 | } 11 | } 12 | return res; 13 | } 14 | 15 | var unObj = function(arr) { 16 | var res = []; 17 | var json = {}; 18 | for (var i = 0, el; 19 | (el = arr[i]), el != null; i++) { 20 | var key = el.route + el.method 21 | if (key && !json[key]) { 22 | res.push(el); 23 | json[key] = 1; 24 | } 25 | } 26 | return res; 27 | } 28 | module.exports.unique = unique; 29 | module.exports.unObj = unObj; -------------------------------------------------------------------------------- /lib/mysql.js: -------------------------------------------------------------------------------- 1 | // 引入mysql数据库模块 2 | var mysql = require('mysql'); 3 | 4 | var logic = require('./logic'); 5 | // 引入异步库 6 | var Q = require('q'); 7 | 8 | var username = ''; 9 | // 数据库封装 10 | module.exports = function(config, rbac) { 11 | var _this = this; 12 | _this.poolConn = mysql.createPool(config); 13 | 14 | _this.query = function(sql, param) { 15 | var deferred = Q.defer(); 16 | param = param || []; 17 | _this.poolConn.getConnection(function(error, conn) { 18 | if (error) { 19 | deferred.reject({ 20 | errCode: error.sqlState, 21 | errMag: error.code 22 | }); 23 | } else { 24 | 25 | conn.query(sql, param, function(error, results, fields) { 26 | if (error) { 27 | deferred.reject({ 28 | errCode: error.sqlState, 29 | errMag: error.code 30 | }); 31 | } else { 32 | deferred.resolve({ 33 | error: error, 34 | results: results, 35 | fields: fields 36 | }); 37 | } 38 | 39 | }); 40 | //释放连接 41 | conn.release(); 42 | } 43 | 44 | }); 45 | return deferred.promise; 46 | }; 47 | 48 | 49 | // 查询用户对应的角色id 50 | _this.queryuser = function(id) { 51 | var deferred = Q.defer(); 52 | if (!id) { 53 | throw new Error('缺少配置参数 : user_key! '); 54 | } else { 55 | _this.query('select ' + rbac.userTrole + ', ' + rbac.userName + ' from ' + rbac.user + ' where id = ' + id).then(function(data) { 56 | deferred.resolve(data.results); 57 | }, function(error) { 58 | deferred.reject(error); 59 | }); 60 | } 61 | return deferred.promise; 62 | } 63 | 64 | // 查询用户对应的角色id 65 | _this.queryrole = function(idarr) { 66 | var deferred = Q.defer(); 67 | var idparam = []; 68 | for (var i = 0; i < idarr.length; i++) { 69 | idparam.push(idarr[i][rbac.userTrole]); 70 | username = idarr[i][rbac.userName]; 71 | } 72 | if (idparam.length <= 0) { 73 | deferred.resolve(idparam); 74 | } else { 75 | // 去重 76 | idparam = logic.unique(idparam.join(',').split(',')).join(','); 77 | _this.query('select ' + rbac.roleTnode + ' from ' + rbac.role + ' where id in (' + idparam + ')').then(function(data) { 78 | deferred.resolve(data.results); 79 | }, function(error) { 80 | deferred.reject(error); 81 | }); 82 | } 83 | return deferred.promise; 84 | } 85 | 86 | // 查询角色对应的权限路由 87 | _this.querynode = function(idarr) { 88 | var deferred = Q.defer(); 89 | var idparam = []; 90 | for (var i = 0; i < idarr.length; i++) { 91 | idparam.push(idarr[i][rbac.roleTnode]); 92 | } 93 | if (idparam.length <= 0) { 94 | deferred.resolve(idparam); 95 | } else { 96 | 97 | idparam = logic.unique(idparam.join(',').split(',')).join(','); 98 | var sql = 'select ' + rbac.nodeTroute + ', ' + rbac.nodeTmethod + ' from ' + rbac.node + ' where id in (' + idparam + ')'; 99 | _this.query(sql).then(function(data) { 100 | deferred.resolve(data.results); 101 | }, function(error) { 102 | deferred.reject(error); 103 | }); 104 | 105 | } 106 | 107 | return deferred.promise; 108 | } 109 | 110 | // 导出权限路由 111 | _this.onlyRoot = function(userid) { 112 | var deferred = Q.defer(); 113 | _this.queryuser(userid) 114 | .then(_this.queryrole) 115 | .then(_this.querynode) 116 | .then(function(data) { 117 | var idparam = []; 118 | for (var i = 0; i < data.length; i++) { 119 | idparam.push({ 120 | route: data[i][rbac.nodeTroute], 121 | method: data[i][rbac.nodeTmethod] || 'GET/POST', 122 | }); 123 | } 124 | if (idparam.length > 0) { 125 | // 去重 126 | idparam = logic.unObj(idparam); 127 | 128 | } 129 | deferred.resolve({ user: username, route: idparam }); 130 | }, function(error) { 131 | deferred.reject(error); 132 | }); 133 | return deferred.promise; 134 | } 135 | 136 | return { 137 | onlyRoot: _this.onlyRoot 138 | } 139 | } -------------------------------------------------------------------------------- /lib/rbac.js: -------------------------------------------------------------------------------- 1 | // 数据库封装 2 | var mysql = require('./mysql'); 3 | 4 | // 默认配置 5 | var config = require('./default'); 6 | 7 | 8 | 9 | 10 | module.exports = function(param) { 11 | param = param || {}; 12 | var _this = this; 13 | // 合并默认参数 14 | _this.default = Object.assign(config, param); 15 | // 配置数据库参数 16 | _this.mysql = mysql(_this.default.mysql, _this.default.rbac); 17 | return function(req, res, next) { 18 | // 检测express-session 是否存在 19 | if (!req.session) { 20 | throw new Error("Cannot find module 'express-session'!"); 21 | } 22 | req.session[_this.default.hook.rbac_route] = req.session[_this.default.hook.rbac_route] || {}; 23 | // 检测是否处于登录状态 24 | if (!req.session[_this.default.rbac.user_key]) { 25 | req[_this.default.hook.root] = function() { 26 | req.session[_this.default.hook.rbac_route] = {}; 27 | return false; 28 | } 29 | req.session[_this.default.hook.rbac_route] = {}; 30 | return next(); 31 | } 32 | // 将权限状态挂载到req参数中 33 | req[_this.default.hook.root] = function(callbask) { 34 | // 获取当前路由 end 35 | var ddpBaseUrl = req.baseUrl; 36 | var ddpPath = (ddpBaseUrl || '') + req.route.path; 37 | var ddpMethod = req.method; 38 | // 获取当前路由 end 39 | // 在session参数挂载权限, 区分是否(1:登录验证, 2:时时验证) 状态 40 | if (req.session[_this.default.hook.rbac_route].route && _this.default.rbac.rbac_type == 1) { 41 | if (_this.default.rbac.superUser && req.session[_this.default.hook.rbac_route].user === _this.default.rbac.superUser) { 42 | return callbask(true); 43 | } 44 | for (var i = 0, el; 45 | (el = req.session[_this.default.hook.rbac_route].route[i]), el != null; i++) { 46 | if (el.method.split('/').indexOf(ddpMethod) != '-1' && el.route === ddpPath) { 47 | return callbask(true); 48 | } 49 | } 50 | return callbask(false); 51 | } 52 | // 从数据库获取路由数组 53 | _this.mysql.onlyRoot(req.session[_this.default.rbac.user_key]).then(function(data) { 54 | req.session[_this.default.hook.rbac_route] = data; 55 | if (_this.default.rbac.superUser && data.user === _this.default.rbac.superUser) { 56 | return callbask(true); 57 | } 58 | // 匹配权限状态 59 | for (var i = 0, el; 60 | (el = data.route[i]), el != null; i++) { 61 | if (el.method.split('/').indexOf(ddpMethod) != '-1' && el.route === ddpPath) { 62 | return callbask(true); 63 | } 64 | } 65 | return callbask(false); 66 | }, function(err) { 67 | console.error(err); 68 | req.session[_this.default.hook.rbac_route] = {}; 69 | return callbask(false); 70 | }); 71 | } 72 | next(); 73 | } 74 | 75 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mysql_rbac", 3 | "version": "1.4.0", 4 | "description": "", 5 | "main": "lib/rbac.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [ 10 | "rbac" 11 | ], 12 | "author": "邓登攀", 13 | "license": "ISC", 14 | "dependencies": { 15 | "mysql": "^2.13.0", 16 | "q": "^1.4.1" 17 | }, 18 | "repository": { 19 | "type": "git", 20 | "url": "https://github.com/pandashuai/mysql_rbac.git" 21 | }, 22 | "bugs": { 23 | "url": "https://github.com/pandashuai/mysql_rbac/issues" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /test/README.md: -------------------------------------------------------------------------------- 1 | # 一个测试mysql_rbac 模块的小案例 2 | 3 | ### 第一步 4 | 5 | ```sh 6 | 7 | npm install 8 | 9 | ``` 10 | 11 | ### 第二步 12 | 13 | ```sh 14 | 15 | 在根目录找到rbac.sql数据库文件并导入mysql数据库 16 | 17 | ``` 18 | 19 | ### 第三步 20 | 21 | ```sh 22 | 23 | 在database目录找到config.js文件 配置数据库参数 24 | 25 | ``` 26 | 27 | ### 第四步启动服务器 28 | 29 | ```sh 30 | 31 | node ./www/bin 32 | 33 | ``` 34 | 35 | ### 最后访问 36 | 37 | ```sh 38 | 39 | http://localhost:3000/ 40 | 41 | ``` 42 | 43 | 44 | ### 后台密码 45 | 46 | ```sh 47 | 48 | 管理员: U: admin P: 123456 49 | 50 | 还有几个账号密码可在用户列表查看, 51 | 注意: 由于是只是测试,所以并没有加密密码,现密码处于明文状态 52 | ``` -------------------------------------------------------------------------------- /test/app.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var path = require('path'); 3 | var favicon = require('serve-favicon'); 4 | var logger = require('morgan'); 5 | var cookieParser = require('cookie-parser'); 6 | var bodyParser = require('body-parser'); 7 | 8 | // ------------------------------------------------- 9 | // 引入模块 10 | var session = require('express-session'); 11 | var rbac = require('../lib/rbac'); 12 | var mysqyconfig = require('./database/config'); 13 | // ------------------------------------------------- 14 | 15 | var index = require('./routes/index'); 16 | // var users = require('./routes/users'); 17 | var admin = require('./routes/admin'); 18 | 19 | var app = express(); 20 | 21 | // view engine setup 22 | app.set('views', path.join(__dirname, 'views')); 23 | app.set('view engine', 'pug'); 24 | 25 | // uncomment after placing your favicon in /public 26 | //app.use(favicon(path.join(__dirname, 'public', 'favicon.ico'))); 27 | app.use(logger('dev')); 28 | app.use(bodyParser.json()); 29 | app.use(bodyParser.urlencoded({ extended: false })); 30 | app.use(cookieParser()); 31 | app.use(express.static(path.join(__dirname, 'public'))); 32 | 33 | // ------------------------------------------------- 34 | // 设置并配置sessions 35 | app.use(session({ 36 | secret: 'mysql_rbac@', 37 | cookie: { maxAge: 60 * 60 * 24 * 1000 }, 38 | name: 'NODESESSID' 39 | })); 40 | 41 | // 设置并配置rbac 42 | app.use(rbac({ 43 | rbac: { 44 | 'rbac_type': 1, //验证类型(1:登录验证, 2:时时验证) *可选 默认为1 45 | 'user_key': 'uid', //用户认证识别号 *可选 默认为uid 46 | 'role': 'role', //角色表名称 *可选 默认为role 47 | 'node': 'node', //权限表名称 *可选 默认为node 48 | 'user': 'user', //用户表名称 *可选 默认为user 49 | 'userTrole': 'role_id', //在用户表储存角色表id的字段 *可选 默认为role_id 50 | 'roleTnode': 'node_id', //在角色表储存权限表id的字段 *可选 默认为node_id 51 | 'nodeTroute': 'route', //在权限表储存判断权限路由的字段 *可选 默认为route 52 | 'nodeTmethod': 'method', //在权限表储存判断权限路由方法的字段 *可选 默认为method , 53 | 'userName': 'username', //用户表的用户名字段 *可选 默认为username , 54 | 'superUser': 'admin' //用户表的用户名,该用户拥有最高权限 *可选 默认为空 55 | }, 56 | mysql: { 57 | 'host': mysqyconfig.host, //IP/域名 *可选 默认为127.0.0.1 58 | 'user': mysqyconfig.user, //数据库账号 *可选 默认为root 59 | 'password': mysqyconfig.password, //数据库密码 *可选 默认为root 60 | 'database': mysqyconfig.database, //数据库库名 *可选 默认为mysq_rbac 61 | 'port': mysqyconfig.port // 端口 *可选 默认为3306 62 | }, 63 | hook: { 64 | // 在req参数挂载权限状态 如: req.is_root *可选 默认为is_root 65 | root: 'is_root', 66 | // 在session参数挂载权限, 区分是否(1:登录验证, 2:时时验证) 状态 如: req.session.rbac_route *可选 默认为rbac_route 67 | rbac_route: 'rbac_route' 68 | } 69 | })); 70 | // ------------------------------------------------- 71 | app.use((req, res, next) => { 72 | // 索引值 73 | res.locals.indexType = req.query.indextype || 'null'; 74 | res.setHeader('Content-Type', 'text/html;charset=utf-8'); 75 | next(); 76 | }); 77 | app.use('/', index); 78 | 79 | app.use('/admin', function(req, res, next) { 80 | // 此req.session.uid 要配置在rbac.rbac.user_key 81 | if (!req.session.uid) { 82 | return res.redirect('/login'); 83 | } 84 | next(); 85 | }); 86 | 87 | // app.use('/users', users); 88 | // 89 | app.use('/admin', admin); 90 | 91 | // catch 404 and forward to error handler 92 | app.use(function(req, res, next) { 93 | var err = new Error('Not Found'); 94 | err.status = 404; 95 | next(err); 96 | }); 97 | 98 | // error handler 99 | app.use(function(err, req, res, next) { 100 | // set locals, only providing error in development 101 | res.locals.message = err.message; 102 | res.locals.error = req.app.get('env') === 'development' ? err : {}; 103 | 104 | // render the error page 105 | res.status(err.status || 500); 106 | res.render('error'); 107 | }); 108 | 109 | console.log('访问: http://localhost:3000'); 110 | module.exports = app; -------------------------------------------------------------------------------- /test/bin/www: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var app = require('../app'); 8 | var debug = require('debug')('test:server'); 9 | var http = require('http'); 10 | 11 | /** 12 | * Get port from environment and store in Express. 13 | */ 14 | 15 | var port = normalizePort(process.env.PORT || '3000'); 16 | app.set('port', port); 17 | 18 | /** 19 | * Create HTTP server. 20 | */ 21 | 22 | var server = http.createServer(app); 23 | 24 | /** 25 | * Listen on provided port, on all network interfaces. 26 | */ 27 | 28 | server.listen(port); 29 | server.on('error', onError); 30 | server.on('listening', onListening); 31 | 32 | /** 33 | * Normalize a port into a number, string, or false. 34 | */ 35 | 36 | function normalizePort(val) { 37 | var port = parseInt(val, 10); 38 | 39 | if (isNaN(port)) { 40 | // named pipe 41 | return val; 42 | } 43 | 44 | if (port >= 0) { 45 | // port number 46 | return port; 47 | } 48 | 49 | return false; 50 | } 51 | 52 | /** 53 | * Event listener for HTTP server "error" event. 54 | */ 55 | 56 | function onError(error) { 57 | if (error.syscall !== 'listen') { 58 | throw error; 59 | } 60 | 61 | var bind = typeof port === 'string' 62 | ? 'Pipe ' + port 63 | : 'Port ' + port; 64 | 65 | // handle specific listen errors with friendly messages 66 | switch (error.code) { 67 | case 'EACCES': 68 | console.error(bind + ' requires elevated privileges'); 69 | process.exit(1); 70 | break; 71 | case 'EADDRINUSE': 72 | console.error(bind + ' is already in use'); 73 | process.exit(1); 74 | break; 75 | default: 76 | throw error; 77 | } 78 | } 79 | 80 | /** 81 | * Event listener for HTTP server "listening" event. 82 | */ 83 | 84 | function onListening() { 85 | var addr = server.address(); 86 | var bind = typeof addr === 'string' 87 | ? 'pipe ' + addr 88 | : 'port ' + addr.port; 89 | debug('Listening on ' + bind); 90 | } 91 | -------------------------------------------------------------------------------- /test/database/config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | host: '127.0.0.1', 3 | user: 'root', 4 | password: 'root', 5 | database: 'mysql_rabc', 6 | port: 3306, 7 | multipleStatements: true 8 | } -------------------------------------------------------------------------------- /test/database/mysql.js: -------------------------------------------------------------------------------- 1 | var mysql = require('mysql'); 2 | var mysql_config = require('./config'); 3 | 4 | //使用连接池,提升性能 5 | var poolConn = mysql.createPool(mysql_config); 6 | 7 | // 连接池查询 8 | var query = function(sql, params, callback) { 9 | if (typeof(params) == 'function') { 10 | callback = params; 11 | params = []; 12 | } 13 | poolConn.getConnection(function(err, conn) { 14 | if (err) { 15 | callback(err, null, null); 16 | } else { 17 | 18 | var query = conn.query(sql, params, callback); 19 | //释放连接 20 | conn.release(); 21 | } 22 | 23 | }); 24 | 25 | }; 26 | 27 | 28 | 29 | // 添加用户 30 | var adduser = function(param, callback) { 31 | query('insert into user(username, password, logintime, loginip, role_id) values(?, ?, ?, ?, ?)', param, function(error, results, fields) { 32 | if (error) { 33 | throw error; 34 | } else { 35 | callback(true); 36 | } 37 | }); 38 | }; 39 | 40 | // 添加角色 41 | var addrole = function(param, callback) { 42 | query('insert into role(name, status, remark) values(?, ?, ?)', param, function(error, results, fields) { 43 | if (error) { 44 | throw error; 45 | } 46 | if (callback) { 47 | return callback(true); 48 | } 49 | }); 50 | } 51 | 52 | 53 | // 添加节点分类 54 | var addnodetag = function(param, callback) { 55 | query('insert into node_tag(name) values(?)', param, function(error, results, fields) { 56 | if (error) { 57 | throw error; 58 | } 59 | if (callback) { 60 | return callback(true); 61 | } 62 | }); 63 | } 64 | 65 | // 添加节点 66 | var addnode = function(param, callback) { 67 | query('insert into node(name, route, method, tag) values(?, ?,?, ?)', param, function(error, results, fields) { 68 | if (error) { 69 | throw error; 70 | } 71 | if (callback) { 72 | return callback(true); 73 | } 74 | }); 75 | } 76 | 77 | // 更新角色权限 78 | var updaterole = function(param, callback) { 79 | query('update role set node_id = ? where id = ? ', param, function(error, results, fields) { 80 | if (error) { 81 | throw error; 82 | } 83 | if (callback) { 84 | return callback(true); 85 | } 86 | }); 87 | } 88 | 89 | // 遍历角色 90 | var eachrole = function(param, callback) { 91 | if (typeof(param) == 'function') { 92 | callback = param; 93 | param = ''; 94 | } else { 95 | param = 'where id = ' + param; 96 | } 97 | query('select * from role ' + param, function(error, results, fields) { 98 | if (error) { 99 | throw error; 100 | } 101 | 102 | return callback(results); 103 | }); 104 | } 105 | 106 | // 遍历用户,user.id as user_id, user.loginlock as loginlock, user.loginip as loginip, user.username as username, user.logintime as logintime, role_user.role_id as role_id, role.name as role_name 107 | var eachuser = function(callback) { 108 | query('select id, loginlock, loginip, username, logintime, role_id from user', function(error, results, fields) { 109 | if (error) { 110 | throw error; 111 | } 112 | return callback(results); 113 | }); 114 | } 115 | // 遍历节点分类 116 | var eachnodetag = function(callback) { 117 | query('select * from node_tag', function(error, results, fields) { 118 | if (error) { 119 | throw error; 120 | } 121 | return callback(results); 122 | }); 123 | } 124 | // 遍历节点 125 | var eachnode = function(callback) { 126 | query('select node.id as id, node.name as name, node.route as route, node.method as method, node_tag.name as tag_name from node left join node_tag on node.tag = node_tag.id', function(error, results, fields) { 127 | if (error) { 128 | throw error; 129 | } 130 | return callback(results); 131 | }); 132 | } 133 | 134 | module.exports.adduser = adduser; 135 | module.exports.addrole = addrole; 136 | module.exports.addnodetag = addnodetag; 137 | module.exports.addnode = addnode; 138 | module.exports.updaterole = updaterole; 139 | module.exports.eachrole = eachrole; 140 | module.exports.eachuser = eachuser; 141 | module.exports.eachnodetag = eachnodetag; 142 | module.exports.eachnode = eachnode; 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | // material 159 | var isUser = function(param, callback) { 160 | query('select * from user where username = ? and password = ?', param, function(error, results, fields) { 161 | if (error) { 162 | throw error; 163 | } 164 | if (results.length > 0) { 165 | return callback(results[0].id); 166 | } 167 | 168 | return callback(false); 169 | }); 170 | } 171 | module.exports.isUser = isUser; -------------------------------------------------------------------------------- /test/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "node ./bin/www" 7 | }, 8 | "dependencies": { 9 | "body-parser": "~1.16.0", 10 | "cookie-parser": "~1.4.3", 11 | "debug": "~2.6.0", 12 | "express": "~4.14.1", 13 | "express-session": "^1.15.1", 14 | "moment": "^2.17.1", 15 | "morgan": "~1.7.0", 16 | "mysql": "^2.13.0", 17 | "pug": "~2.0.0-beta10", 18 | "serve-favicon": "~2.3.2" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /test/public/assets/css/dataTables.bootstrap.css: -------------------------------------------------------------------------------- 1 | .DTTTFooter { 2 | margin: 0; 3 | background: #fff; 4 | overflow: hidden; 5 | padding: 5px 5px 2px 10px; 6 | border: 1px solid #ddd; 7 | border-top: 0px; 8 | background-color: #eee; 9 | background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjxzdmcgeG1sbnM9Imh0d…0iMSIgaGVpZ2h0PSIxIiBmaWxsPSJ1cmwoI2xlc3NoYXQtZ2VuZXJhdGVkKSIgLz48L3N2Zz4=); 10 | background-image: -webkit-linear-gradient(top, #f2f2f2 0, #fafafa 100%); 11 | background-image: -moz-linear-gradient(top, #f2f2f2 0, #fafafa 100%); 12 | background-image: linear-gradient(to bottom, #f2f2f2 0, #fafafa 100%); 13 | } 14 | 15 | .DTTTFooter .col-sm-6 { 16 | padding: 0px; 17 | } 18 | 19 | .dataTables_wrapper { 20 | position: relative; 21 | } 22 | 23 | div.dataTables_length { 24 | position: absolute; 25 | top: 0px; 26 | right: 0px; 27 | } 28 | 29 | 30 | div.dataTables_length select { 31 | width: 75px; 32 | } 33 | 34 | div.dataTables_filter label { 35 | font-weight: normal; 36 | float: left; 37 | margin-bottom: 10px; 38 | position: relative; 39 | } 40 | 41 | .dataTables_filter label:before { 42 | font-family: "FontAwesome"; 43 | content: "\f002"; 44 | display: block; 45 | position: absolute; 46 | top: 0; 47 | bottom: 0; 48 | left: 0px; 49 | width: 30px; 50 | max-width: 30px; 51 | overflow: hidden; 52 | color: #5db2ff; 53 | text-align: center; 54 | padding-top: 6px; 55 | } 56 | 57 | .dataTables_filter input { 58 | width: 16em; 59 | padding-left: 28px; 60 | } 61 | 62 | 63 | 64 | div.dataTables_info { 65 | padding-top: 8px; 66 | } 67 | 68 | div.dataTables_paginate { 69 | float: right; 70 | margin: 0; 71 | } 72 | 73 | div.dataTables_paginate ul.pagination { 74 | margin: 2px; 75 | } 76 | 77 | table.table { 78 | clear: both; 79 | max-width: none !important; 80 | } 81 | 82 | table.table thead .sorting, 83 | table.table thead .sorting_asc, 84 | table.table thead .sorting_desc, 85 | table.table thead .sorting_asc_disabled, 86 | table.table thead .sorting_desc_disabled { 87 | cursor: pointer; 88 | } 89 | 90 | table.table thead .sorting { 91 | background: url('../img/sort_both.png') no-repeat center right; 92 | } 93 | 94 | table.table thead .sorting_asc { 95 | background: url('../img/sort_asc.png') no-repeat center right; 96 | } 97 | 98 | table.table thead .sorting_desc { 99 | background: url('../img/sort_desc.png') no-repeat center right; 100 | } 101 | 102 | table.table thead .sorting_asc_disabled { 103 | background: url('../img/sort_asc_disabled.png') no-repeat center right; 104 | } 105 | 106 | table.table thead .sorting_desc_disabled { 107 | background: url('../img/sort_desc_disabled.png') no-repeat center right; 108 | } 109 | 110 | table.dataTable th:active { 111 | outline: none; 112 | } 113 | 114 | /* Scrolling */ 115 | div.dataTables_scrollHead table { 116 | margin-bottom: 0 !important; 117 | border-bottom-left-radius: 0; 118 | border-bottom-right-radius: 0; 119 | } 120 | 121 | div.dataTables_scrollHead table thead tr:last-child th:first-child, 122 | div.dataTables_scrollHead table thead tr:last-child td:first-child { 123 | border-bottom-left-radius: 0 !important; 124 | border-bottom-right-radius: 0 !important; 125 | } 126 | 127 | div.dataTables_scrollBody table { 128 | border-top: none; 129 | margin-bottom: 0 !important; 130 | } 131 | 132 | div.dataTables_scrollBody tbody tr:first-child th, 133 | div.dataTables_scrollBody tbody tr:first-child td { 134 | border-top: none; 135 | } 136 | 137 | div.dataTables_scrollFoot table { 138 | border-top: none; 139 | } 140 | 141 | /* 142 | * TableTools styles 143 | */ 144 | table.DTTT_selectable tbody tr { 145 | cursor: pointer; 146 | } 147 | 148 | .DTTT.btn-group { 149 | position: absolute; 150 | right: 70px; 151 | top: 0px; 152 | } 153 | 154 | div.DTTT .btn { 155 | color: #333 !important; 156 | font-size: 12px; 157 | } 158 | 159 | div.DTTT .btn:hover { 160 | text-decoration: none !important; 161 | } 162 | 163 | ul.DTTT_dropdown.dropdown-menu { 164 | z-index: 2003; 165 | } 166 | 167 | ul.DTTT_dropdown.dropdown-menu a { 168 | color: #333 !important; /* needed only when demo_page.css is included */ 169 | } 170 | 171 | ul.DTTT_dropdown.dropdown-menu li { 172 | position: relative; 173 | } 174 | 175 | ul.DTTT_dropdown.dropdown-menu li:hover a { 176 | background-color: #0088cc; 177 | color: white !important; 178 | } 179 | 180 | /* TableTools information display */ 181 | div.DTTT_print_info.modal { 182 | height: 150px; 183 | margin-top: -75px; 184 | text-align: center; 185 | } 186 | 187 | div.DTTT_print_info h6 { 188 | font-weight: normal; 189 | font-size: 28px; 190 | line-height: 28px; 191 | margin: 1em; 192 | } 193 | 194 | div.DTTT_print_info p { 195 | font-size: 14px; 196 | line-height: 20px; 197 | } 198 | 199 | 200 | 201 | /* 202 | * FixedColumns styles 203 | */ 204 | div.DTFC_LeftHeadWrapper table, 205 | div.DTFC_LeftFootWrapper table, 206 | div.DTFC_RightHeadWrapper table, 207 | div.DTFC_RightFootWrapper table, 208 | table.DTFC_Cloned tr.even { 209 | background-color: white; 210 | } 211 | 212 | div.DTFC_RightHeadWrapper table, 213 | div.DTFC_LeftHeadWrapper table { 214 | margin-bottom: 0 !important; 215 | border-top-right-radius: 0 !important; 216 | border-bottom-left-radius: 0 !important; 217 | border-bottom-right-radius: 0 !important; 218 | } 219 | 220 | div.DTFC_RightHeadWrapper table thead tr:last-child th:first-child, 221 | div.DTFC_RightHeadWrapper table thead tr:last-child td:first-child, 222 | div.DTFC_LeftHeadWrapper table thead tr:last-child th:first-child, 223 | div.DTFC_LeftHeadWrapper table thead tr:last-child td:first-child { 224 | border-bottom-left-radius: 0 !important; 225 | border-bottom-right-radius: 0 !important; 226 | } 227 | 228 | div.DTFC_RightBodyWrapper table, 229 | div.DTFC_LeftBodyWrapper table { 230 | border-top: none; 231 | margin-bottom: 0 !important; 232 | } 233 | 234 | div.DTFC_RightBodyWrapper tbody tr:first-child th, 235 | div.DTFC_RightBodyWrapper tbody tr:first-child td, 236 | div.DTFC_LeftBodyWrapper tbody tr:first-child th, 237 | div.DTFC_LeftBodyWrapper tbody tr:first-child td { 238 | border-top: none; 239 | } 240 | 241 | div.DTFC_RightFootWrapper table, 242 | div.DTFC_LeftFootWrapper table { 243 | border-top: none; 244 | } 245 | 246 | 247 | .dataTable .row-details { 248 | margin-top: 3px; 249 | display: inline-block; 250 | cursor: pointer; 251 | width: 10px; 252 | font-size:14px; 253 | height: 14px; 254 | } 255 | 256 | .dataTable .details { 257 | background-color: #f5f5f5; 258 | } 259 | 260 | .dataTable .details td, 261 | .dataTable .details th { 262 | padding: 4px; 263 | background-color: none; 264 | border: 0; 265 | } 266 | 267 | .dataTable .details tr:hover td, 268 | .dataTable .details tr:hover th { 269 | background-color: none; 270 | } 271 | 272 | .dataTable .details tr:nth-child(odd) td, 273 | .dataTable .details tr:nth-child(odd) th { 274 | background-color: #f5f5f5; 275 | } 276 | 277 | .dataTable .details tr:nth-child(even) td, 278 | .dataTable .details tr:nth-child(even) th { 279 | background-color: #f5f5f5; 280 | } 281 | -------------------------------------------------------------------------------- /test/public/assets/css/demo.min.css: -------------------------------------------------------------------------------- 1 | .fontawesome-icon-list{margin:22px 0 0 0!important}.fontawesome-icon-list .fa-hover{color:#262626;display:block;height:36px;line-height:32px;padding-left:10px;padding:2px 0}.fontawesome-icon-list .fa-hover .fa{display:inline-block;font-size:14px;margin-right:10px;text-align:right;width:32px}.fontawesome-icon-list .fa-hover:hover{background-color:#eee;color:#000;text-decoration:none}.fontawesome-icon-list .fa-hover:hover .fa{font-size:28px;vertical-align:-6px}.bs-glyphicons{overflow:hidden}.bs-glyphicons li{float:left;width:25%;height:115px;padding:10px;font-size:10px;line-height:1.4;text-align:center;border:1px solid #fff;background-color:#f9f9f9}.bs-glyphicons li:hover{color:#fff;background-color:#e46f61}.bs-glyphicons .glyphicon{margin-top:5px;margin-bottom:10px;font-size:24px}.bs-glyphicons .glyphicon-class{display:block;text-align:center;word-wrap:break-word}.bs-glyphicons-list{padding-left:0;list-style:none}@media(min-width:768px){.bs-glyphicons{margin-left:0;margin-right:0}.bs-glyphicons li{width:12.5%;font-size:12px}}.weathericons{padding:5px 10px;border-radius:4px}.weathericons:hover{background-color:#a0d468;color:#fff}.weathericons .icon,.weathericons .class{display:inline-block}.weathericons .icon{margin-right:5px;font-size:24px}#typicon-preview{margin:0 auto;position:relative;text-align:center}#typicon-preview .icon{float:left;padding:6px;display:inline-block;cursor:pointer;width:48px;height:48px;text-align:center;vertical-align:middle;-webkit-border-radius:6px;-webkit-background-clip:padding-box;-moz-border-radius:6px;-moz-background-clip:padding;border-radius:6px;background-clip:padding-box;color:#444}#typicon-preview .icon .typcn:before{font-size:24px}#typicon-preview .icon:hover{background-color:#ffce55;line-height:38px;color:#fff}#typicon-preview .icon:hover .typcn:before{font-size:32px}.chartcontainer{width:100%;text-align:center}.easy-pie-chart-preview .well{width:100%;height:250px;text-align:center;position:relative;padding-top:50px}.easy-pie-chart-preview .well .easyPieChart{margin:0 auto}.sparkline-preview .well{text-align:center;position:relative}.buttons-preview .btn,.buttons-preview .btn-group{margin-bottom:10px;margin-right:10px}.dropdown-container{margin:0 auto;text-align:center}.dropdown-container .dropdown-preview{display:inline-block;text-align:left}.dropdown-container .dropdown-preview>.dropdown-menu{display:block;position:static;margin-bottom:5px}#dropdownbuttons .btn{margin-top:10px}.labels-container .label{margin:0 10px 10px 0}.popover-container{overflow:auto}.popoverexample .popover{position:relative;display:block;width:260px;margin:20px}.modal-preview .modal{position:relative;top:auto;right:auto;left:auto;bottom:auto;z-index:1;display:block;width:auto;overflow:hidden;max-width:600px}.modal-preview .modal .modal-dialog{width:90%}.bordered-well-container .well{margin-bottom:0}.knob-container{text-align:center}#red,#green,#blue{margin:10px;display:inline-block;height:200px}#colorpicker{height:240px;width:100%;margin:20px auto;padding:10px;border:1px solid #bfbfbf}#colorpicker .result{margin:60px 30px;height:100px;width:100px;display:inline-block;vertical-align:top;color:#7f7f7f;background:#7f7f7f;border:1px solid #fff;box-shadow:0 0 10px}#colors .colored-slider{margin-bottom:20px}#sizes .sized-slider{margin-bottom:20px}.grid-example .row{margin-bottom:15px;padding:0 15px}.grid-example .row [class^=col-]{padding-top:10px;padding-bottom:10px;background-color:#f5f5f5;border:1px solid #ddd} -------------------------------------------------------------------------------- /test/public/assets/css/weather-icons.min.css: -------------------------------------------------------------------------------- 1 | @font-face{font-family:'weather';src:url('../fonts/weathericons-regular-webfont.eot');src:url('../fonts/weathericons-regular-webfont.eot?#iefix') format('embedded-opentype'),url('../fonts/weathericons-regular-webfont.woff') format('woff'),url('../fonts/weathericons-regular-webfont.ttf') format('truetype'),url('../fonts/weathericons-regular-webfont.svg#weathericons-regular-webfontRg') format('svg');font-weight:normal;font-style:normal}[class^="wi-"],[class*=" wi-"]{font-family:weather;font-weight:normal;font-style:normal;text-decoration:inherit;text-transform:none;-webkit-font-smoothing:antialiased;*margin-right:.3em}[class^="wi-"]:before,[class*=" wi-"]:before{text-decoration:inherit;display:inline-block;speak:none}.wi-day-cloudy-gusts:before{content:""}.wi-day-cloudy-windy:before{content:""}.wi-day-cloudy:before{content:""}.wi-day-fog:before{content:""}.wi-day-hail:before{content:""}.wi-day-lightning:before{content:""}.wi-day-rain-mix:before{content:""}.wi-day-rain-wind:before{content:""}.wi-day-rain:before{content:""}.wi-day-showers:before{content:""}.wi-day-snow:before{content:""}.wi-day-sprinkle:before{content:""}.wi-day-sunny-overcast:before{content:""}.wi-day-sunny:before{content:""}.wi-day-storm-showers:before{content:""}.wi-day-thunderstorm:before{content:""}.wi-cloudy-gusts:before{content:""}.wi-cloudy-windy:before{content:""}.wi-cloudy:before{content:""}.wi-fog:before{content:""}.wi-hail:before{content:""}.wi-lightning:before{content:""}.wi-rain-mix:before{content:""}.wi-rain-wind:before{content:""}.wi-rain:before{content:""}.wi-showers:before{content:""}.wi-snow:before{content:""}.wi-sprinkle:before{content:""}.wi-storm-showers:before{content:""}.wi-thunderstorm:before{content:""}.wi-windy:before{content:""}.wi-night-alt-cloudy-gusts:before{content:""}.wi-night-alt-cloudy-windy:before{content:""}.wi-night-alt-hail:before{content:""}.wi-night-alt-lightning:before{content:""}.wi-night-alt-rain-mix:before{content:""}.wi-night-alt-rain-wind:before{content:""}.wi-night-alt-rain:before{content:""}.wi-night-alt-showers:before{content:""}.wi-night-alt-snow:before{content:""}.wi-night-alt-sprinkle:before{content:""}.wi-night-alt-storm-showers:before{content:""}.wi-night-alt-thunderstorm:before{content:""}.wi-night-clear:before{content:""}.wi-night-cloudy-gusts:before{content:""}.wi-night-cloudy-windy:before{content:""}.wi-night-cloudy:before{content:""}.wi-night-hail:before{content:""}.wi-night-lightning:before{content:""}.wi-night-rain-mix:before{content:""}.wi-night-rain-wind:before{content:""}.wi-night-rain:before{content:""}.wi-night-showers:before{content:""}.wi-night-snow:before{content:""}.wi-night-sprinkle:before{content:""}.wi-night-storm-showers:before{content:""}.wi-night-thunderstorm:before{content:""}.wi-celcius:before{content:""}.wi-cloud-down:before{content:""}.wi-cloud-refresh:before{content:""}.wi-cloud-up:before{content:""}.wi-cloud:before{content:""}.wi-degrees:before{content:""}.wi-down-left:before{content:""}.wi-down:before{content:""}.wi-fahrenheit:before{content:""}.wi-horizon-alt:before{content:""}.wi-horizon:before{content:""}.wi-left:before{content:""}.wi-night-fog:before{content:""}.wi-refresh-alt:before{content:""}.wi-refresh:before{content:""}.wi-right:before{content:""}.wi-sprinkles:before{content:""}.wi-strong-wind:before{content:""}.wi-sunrise:before{content:""}.wi-sunset:before{content:""}.wi-thermometer-exterior:before{content:""}.wi-thermometer-internal:before{content:""}.wi-thermometer:before{content:""}.wi-tornado:before{content:""}.wi-up-right:before{content:""}.wi-up:before{content:""}.wi-wind-east:before{content:""}.wi-wind-north-east:before{content:""}.wi-wind-north-west:before{content:""}.wi-wind-north:before{content:""}.wi-wind-south-east:before{content:""}.wi-wind-south-west:before{content:""}.wi-wind-south:before{content:""}.wi-wind-west:before{content:""} -------------------------------------------------------------------------------- /test/public/assets/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandashuai/mysql_rbac/e8cf4cc337e56d5dcfacb93a9b6a6f82b089f3c8/test/public/assets/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /test/public/assets/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandashuai/mysql_rbac/e8cf4cc337e56d5dcfacb93a9b6a6f82b089f3c8/test/public/assets/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /test/public/assets/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandashuai/mysql_rbac/e8cf4cc337e56d5dcfacb93a9b6a6f82b089f3c8/test/public/assets/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /test/public/assets/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandashuai/mysql_rbac/e8cf4cc337e56d5dcfacb93a9b6a6f82b089f3c8/test/public/assets/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /test/public/assets/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandashuai/mysql_rbac/e8cf4cc337e56d5dcfacb93a9b6a6f82b089f3c8/test/public/assets/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /test/public/assets/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandashuai/mysql_rbac/e8cf4cc337e56d5dcfacb93a9b6a6f82b089f3c8/test/public/assets/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /test/public/assets/fonts/typicons.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandashuai/mysql_rbac/e8cf4cc337e56d5dcfacb93a9b6a6f82b089f3c8/test/public/assets/fonts/typicons.eot -------------------------------------------------------------------------------- /test/public/assets/fonts/typicons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandashuai/mysql_rbac/e8cf4cc337e56d5dcfacb93a9b6a6f82b089f3c8/test/public/assets/fonts/typicons.ttf -------------------------------------------------------------------------------- /test/public/assets/fonts/typicons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandashuai/mysql_rbac/e8cf4cc337e56d5dcfacb93a9b6a6f82b089f3c8/test/public/assets/fonts/typicons.woff -------------------------------------------------------------------------------- /test/public/assets/fonts/weathericons-regular-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandashuai/mysql_rbac/e8cf4cc337e56d5dcfacb93a9b6a6f82b089f3c8/test/public/assets/fonts/weathericons-regular-webfont.eot -------------------------------------------------------------------------------- /test/public/assets/fonts/weathericons-regular-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandashuai/mysql_rbac/e8cf4cc337e56d5dcfacb93a9b6a6f82b089f3c8/test/public/assets/fonts/weathericons-regular-webfont.ttf -------------------------------------------------------------------------------- /test/public/assets/fonts/weathericons-regular-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandashuai/mysql_rbac/e8cf4cc337e56d5dcfacb93a9b6a6f82b089f3c8/test/public/assets/fonts/weathericons-regular-webfont.woff -------------------------------------------------------------------------------- /test/public/assets/img/attach-blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandashuai/mysql_rbac/e8cf4cc337e56d5dcfacb93a9b6a6f82b089f3c8/test/public/assets/img/attach-blue.png -------------------------------------------------------------------------------- /test/public/assets/img/attach-green.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandashuai/mysql_rbac/e8cf4cc337e56d5dcfacb93a9b6a6f82b089f3c8/test/public/assets/img/attach-green.png -------------------------------------------------------------------------------- /test/public/assets/img/attach-red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandashuai/mysql_rbac/e8cf4cc337e56d5dcfacb93a9b6a6f82b089f3c8/test/public/assets/img/attach-red.png -------------------------------------------------------------------------------- /test/public/assets/img/attach-yellow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandashuai/mysql_rbac/e8cf4cc337e56d5dcfacb93a9b6a6f82b089f3c8/test/public/assets/img/attach-yellow.png -------------------------------------------------------------------------------- /test/public/assets/img/avatars/Javi-Jimenez.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandashuai/mysql_rbac/e8cf4cc337e56d5dcfacb93a9b6a6f82b089f3c8/test/public/assets/img/avatars/Javi-Jimenez.jpg -------------------------------------------------------------------------------- /test/public/assets/img/avatars/John-Smith.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandashuai/mysql_rbac/e8cf4cc337e56d5dcfacb93a9b6a6f82b089f3c8/test/public/assets/img/avatars/John-Smith.jpg -------------------------------------------------------------------------------- /test/public/assets/img/avatars/Matt-Cheuvront.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandashuai/mysql_rbac/e8cf4cc337e56d5dcfacb93a9b6a6f82b089f3c8/test/public/assets/img/avatars/Matt-Cheuvront.jpg -------------------------------------------------------------------------------- /test/public/assets/img/avatars/Nicolai-Larson.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandashuai/mysql_rbac/e8cf4cc337e56d5dcfacb93a9b6a6f82b089f3c8/test/public/assets/img/avatars/Nicolai-Larson.jpg -------------------------------------------------------------------------------- /test/public/assets/img/avatars/Osvaldus-Valutis.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandashuai/mysql_rbac/e8cf4cc337e56d5dcfacb93a9b6a6f82b089f3c8/test/public/assets/img/avatars/Osvaldus-Valutis.jpg -------------------------------------------------------------------------------- /test/public/assets/img/avatars/Sergey-Azovskiy.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandashuai/mysql_rbac/e8cf4cc337e56d5dcfacb93a9b6a6f82b089f3c8/test/public/assets/img/avatars/Sergey-Azovskiy.jpg -------------------------------------------------------------------------------- /test/public/assets/img/avatars/Stephanie-Walter.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandashuai/mysql_rbac/e8cf4cc337e56d5dcfacb93a9b6a6f82b089f3c8/test/public/assets/img/avatars/Stephanie-Walter.jpg -------------------------------------------------------------------------------- /test/public/assets/img/avatars/adam-jansen.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandashuai/mysql_rbac/e8cf4cc337e56d5dcfacb93a9b6a6f82b089f3c8/test/public/assets/img/avatars/adam-jansen.jpg -------------------------------------------------------------------------------- /test/public/assets/img/avatars/bing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandashuai/mysql_rbac/e8cf4cc337e56d5dcfacb93a9b6a6f82b089f3c8/test/public/assets/img/avatars/bing.png -------------------------------------------------------------------------------- /test/public/assets/img/avatars/divyia.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandashuai/mysql_rbac/e8cf4cc337e56d5dcfacb93a9b6a6f82b089f3c8/test/public/assets/img/avatars/divyia.jpg -------------------------------------------------------------------------------- /test/public/assets/img/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandashuai/mysql_rbac/e8cf4cc337e56d5dcfacb93a9b6a6f82b089f3c8/test/public/assets/img/favicon.png -------------------------------------------------------------------------------- /test/public/assets/img/jquery.minicolors.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandashuai/mysql_rbac/e8cf4cc337e56d5dcfacb93a9b6a6f82b089f3c8/test/public/assets/img/jquery.minicolors.png -------------------------------------------------------------------------------- /test/public/assets/img/logo-inverted.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandashuai/mysql_rbac/e8cf4cc337e56d5dcfacb93a9b6a6f82b089f3c8/test/public/assets/img/logo-inverted.png -------------------------------------------------------------------------------- /test/public/assets/img/logo-rtl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandashuai/mysql_rbac/e8cf4cc337e56d5dcfacb93a9b6a6f82b089f3c8/test/public/assets/img/logo-rtl.png -------------------------------------------------------------------------------- /test/public/assets/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandashuai/mysql_rbac/e8cf4cc337e56d5dcfacb93a9b6a6f82b089f3c8/test/public/assets/img/logo.png -------------------------------------------------------------------------------- /test/public/assets/img/sort_asc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandashuai/mysql_rbac/e8cf4cc337e56d5dcfacb93a9b6a6f82b089f3c8/test/public/assets/img/sort_asc.png -------------------------------------------------------------------------------- /test/public/assets/img/sort_asc_disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandashuai/mysql_rbac/e8cf4cc337e56d5dcfacb93a9b6a6f82b089f3c8/test/public/assets/img/sort_asc_disabled.png -------------------------------------------------------------------------------- /test/public/assets/img/sort_both.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandashuai/mysql_rbac/e8cf4cc337e56d5dcfacb93a9b6a6f82b089f3c8/test/public/assets/img/sort_both.png -------------------------------------------------------------------------------- /test/public/assets/img/sort_desc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandashuai/mysql_rbac/e8cf4cc337e56d5dcfacb93a9b6a6f82b089f3c8/test/public/assets/img/sort_desc.png -------------------------------------------------------------------------------- /test/public/assets/img/sort_desc_disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandashuai/mysql_rbac/e8cf4cc337e56d5dcfacb93a9b6a6f82b089f3c8/test/public/assets/img/sort_desc_disabled.png -------------------------------------------------------------------------------- /test/public/assets/img/temp1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandashuai/mysql_rbac/e8cf4cc337e56d5dcfacb93a9b6a6f82b089f3c8/test/public/assets/img/temp1.png -------------------------------------------------------------------------------- /test/public/assets/js/beyond.js: -------------------------------------------------------------------------------- 1 | 2 |