diff --git a/README.md b/README.md index 25c2411..79ac5b9 100644 --- a/README.md +++ b/README.md @@ -1 +1,17 @@ # User Authentification and role management REST Webservice + +## Description + +Usr provides you a webservice to authenticate and manage your users with a REST API : + +- Using EveryAuth to enable your users to login with any credentials +- Using REST to be shared within multiples service. + +## Authentification of a user : + +1/ You deploy your service to auth.yourdomain.com +2/ On your application, to authentificate a user, just need to : + - redirect the user to http://auth.yourdomain.com/login/http://mynewapp.com/loguedId/ + - The user will come back to http://mynewapp.com/loguedId/SUPERTOKEN with a token + - Send a request to get all the details about your user. + diff --git a/configs.coffee b/examples/configs.coffee similarity index 100% rename from configs.coffee rename to examples/configs.coffee diff --git a/examples/myappnewapp.coffee b/examples/myappnewapp.coffee new file mode 100644 index 0000000..f3a65d1 --- /dev/null +++ b/examples/myappnewapp.coffee @@ -0,0 +1,37 @@ +express = require 'express' +$ = require 'jquery' + +app = express.createServer( + express.bodyParser(), + express.favicon(), + express.cookieParser(), + express.session({ secret: 'supersecret'}), +) + +myAppUrl = 'http://127.0.0.1:3001' +usrAppToken = 'lalalal' +usrUrl = "http://local.host:3000" + + +app.get('/', (req, res)-> + if req.session.user + user = req.session.user + res.send("Welcome : #{user.id}, you are in the groups : #{user.groups.join(',')}logout") + else + res.send("login") +) + +app.get('/logout', (req, res)-> + delete(req.session.user) + res.redirect(usrUrl+"/logout/#{myAppUrl}") +) + +app.get('/logguedIn/:token', (req, res)-> + url = usrUrl+"/info/#{req.params.token}/#{usrAppToken}" + $.getJSON(url,(datas)-> + req.session.user = datas + res.redirect('/') + ) +) + +app.listen(3001) diff --git a/lib/exec.coffee b/examples/server.coffee similarity index 82% rename from lib/exec.coffee rename to examples/server.coffee index ca5ce44..d19aff6 100644 --- a/lib/exec.coffee +++ b/examples/server.coffee @@ -2,19 +2,18 @@ Async = require 'async' Log = require 'log' log = new Log('warning') -configs = require '../configs' +configs = require './configs' express = require 'express' app = express.createServer( express.bodyParser(), - express.static(__dirname + "/public"), express.favicon(), express.cookieParser(), express.session({ secret: 'supersecret'}), ) app.log = log -Auth = require './app' +Auth = require '../index' auth = new Auth(app,configs) app.get('/', (req, res)-> @@ -29,4 +28,3 @@ app.configure(()-> app.listen(configs.app.port) log.info __dirname log.info 'Application started http://local.host:'+configs.app.port -module.exports = app diff --git a/index.js b/index.js index 7605f9e..f97237a 100644 --- a/index.js +++ b/index.js @@ -1,2 +1,2 @@ require('coffee-script') -module.exports = require('./lib/exec.coffee'); +module.exports = require('./lib/app'); diff --git a/lib/app.coffee b/lib/app.coffee index 5d90a5d..3a1e8a0 100644 --- a/lib/app.coffee +++ b/lib/app.coffee @@ -1,9 +1,12 @@ - module.exports = class App constructor : (express,@configs)-> _ = @ - Log = require 'log' - @log = new Log() + if not @configs.logger? + Log = require 'log' + @log = new Log("warning") + else + @log = @configs.logger + EventEmitter = require('eventemitter2').EventEmitter2 @_event = new EventEmitter( wildcard:true @@ -14,17 +17,24 @@ module.exports = class App #!TODO move this function to access... @_event.once('token/new',(datas)-> - console.log "Check INIT ROOT ?" + _.log.debug "First token has been created maybe a root group need to be created ?" _.stores.group.findGroupByName('root',(err,group)-> if group == null - console.log "ROOT NULL ?" _.stores.group.addGroup('_root',(err,groupId)-> + _.emit('group/new', + groupId : groupId + token : datas.token + ) _.stores.group.addUserToGroup(datas.userId,groupId,(err,res)-> - if !res - throw "Error root access granted..." + _.emit('group/addUser', + groupId : groupId + token : datas.token + userId : datas.userId + ) _.stores.group.addUserToGroupCache(datas.userId,groupId,(err,res)-> if !res throw "Error root access granted..." + # Might be a bit strange, but root seems to proclam himself root _.emit('root/new',datas) ) ) @@ -50,7 +60,7 @@ module.exports = class App 'access' : './access/access' 'event' : './event/event' for name,file of modules - console.log "Load #{file} as #{name}" + @log.info "Load #{file} as #{name}" Module = require file @[name] = new Module(@) diff --git a/lib/auth/auth.coffee b/lib/auth/auth.coffee index 2c3d7d4..8cea5d3 100644 --- a/lib/auth/auth.coffee +++ b/lib/auth/auth.coffee @@ -5,6 +5,7 @@ module.exports = class Auth extends Component @._everyAuth() @._routes() + #No Check on addUser, everybody can register or create a new user addUser : (source='', id='', datas={},cb)-> _ = @ store = @app.stores.user @@ -12,7 +13,9 @@ module.exports = class Auth extends Component _.checkErr(err) cb(null,userId) _.emit('user/new', - userId:userId + userId : userId + source : source + id : id ) ) @@ -56,6 +59,17 @@ module.exports = class Auth extends Component _routes : ()-> _ = @ + + #Add a Are you sure on the logout ? + @routeGet('/logout/*', (req, res)-> + req.logout() + delete(req.session.token) + if req.params? and req.params[0]? and req.params[0] != '' + res.redirect(req.params[0]) + return + #!TODO redirect you from where you are coming ? + res.redirect('/')#!TODO RENDER LOGIN PAGE + ) @routeGet('/login/*', (req, res)-> if req.params? and req.params[0]? and req.params[0] != '' req.session.url = req.params[0] @@ -65,6 +79,16 @@ module.exports = class Auth extends Component res.redirect('/auth/local')#!TODO RENDER LOGIN PAGE ) + + #!TODO Check on AppToken + @routeGet('/info/:token/:appToken', (req, res)-> + json = {} + _.app.token.getInfo(req.params.token,req.params.appToken,(err,info)-> + _.checkErr(err) + res.json(info) + ) + + ) @routeGet('/redirect', (req, res)-> if not req.loggedIn res.redirect('/login/') diff --git a/lib/event/event.coffee b/lib/event/event.coffee index c75b8f3..4acc183 100644 --- a/lib/event/event.coffee +++ b/lib/event/event.coffee @@ -4,6 +4,8 @@ module.exports = class Event extends Component @app = app @access = app.access io = require('socket.io').listen(@express()) + io.set('log level', 1) + @channel = io.of('/auth')#!TODO Put in configs... #@channel = io @_init_socket() diff --git a/lib/group/group.coffee b/lib/group/group.coffee index 13a987c..b396772 100644 --- a/lib/group/group.coffee +++ b/lib/group/group.coffee @@ -36,42 +36,55 @@ module.exports = class Group extends Component add : (groupName, token, cb)-> _ = @ - #!TODO Validate Name with Regex + #!TODO Validate Name with Regex and rules + + #Add a new user Group + #Add a new group access group + + #Add a new Group _.app.access.check(token, ['_group_add','_root'],(userId)-> #!TODO check group existence - _.store.addGroup(groupName, (groupId)-> - cb(groupId) - _.event.emit('group:new', + _.store.addGroup(groupName, (err,groupId)-> + cb(err,groupId) + _.emit('group:new', groupId : groupId token : token + groupName : groupName + authorId : userId ) ) ) - addUserToGroup : (groupName, userId, token, cb)-> + addUserToGroup : (userId, groupName, token, cb)-> _ = @ #!TODO Validate Regex _.app.access.check(token, [ - 'group_'+groupName+"_"+owner, - 'group_'+groupName+"_"+add, + 'group_'+groupName+"_add", '_root' ], - (userId)-> - @store.findGroupByName(groupName,(group)-> - @store.addUserToGroup(group.id,userId,cb) - @store.addUserToGroupCache(group.id,userId,()-> - _.event.emit('group:addUser', - groupId : groupId - token : token - ) + (authorId)-> + _.store.findGroupByName(groupName,(err,group)-> + _.checkErr(err) + _.store.addUserToGroup(userId,group.id,cb) + _.emit('group:addUser', + groupId : group.id + token : token + authorId : authorId + userId : userId + groupName : groupName ) + #Add to cache addUserToGroupCache = (groupId, userId)-> - @store.addUserToGroupCache(i,userId,()-> - _.event.emit('group:addUser', - groupId : groupId + _.store.addUserToGroupCache(userId, groupId,(err,res)-> + _.checkErr(err) + _.emit('group:addUserCache', + groupId : group.id token : token + userId : userId + groupName : groupName ) ) + addUserToGroupCache(group.id, userId) for i in group._groups addUserToGroupCache(i, userId) ) diff --git a/lib/token/token.coffee b/lib/token/token.coffee index 718f326..13915bd 100644 --- a/lib/token/token.coffee +++ b/lib/token/token.coffee @@ -29,7 +29,7 @@ module.exports = class Token extends Component ) get : (token, cb)-> - @store.get(token, cb) + @store.getToken(token, cb) add : (userId, options, cb)-> _ = @ _.app.stores.token.addToken( @@ -44,3 +44,28 @@ module.exports = class Token extends Component userId : userId ) ) + #!TODO ADD Check on AppToken + getInfo : (token, appToken, cb)-> + _ = @ + json = {} + _.get(token,(err,datas)-> + if err != null + cb(err,json) + return + _.app.stores.user.findUserById(datas.userId, (err,user)-> + if err != null + cb(err,json) + return + json= user + _.app.stores.group.getGroupsUserIsMemberOf(user.id, (err,groups)-> + if err != null + cb(err,json) + return + json.groups = [] + for k,g of groups + json.groups.push(g.name) + cb(err,json) + return + ) + ) + ) diff --git a/package.json b/package.json index d0d9300..0113829 100644 --- a/package.json +++ b/package.json @@ -16,22 +16,23 @@ ], "main": "index", "dependencies": { - "log": "*", "coffee-script": ">=1.3", - "mongodb": "*", - "everyauth": "*", - "express": "=2.3.10", - "node-promise": "*", - "eventemitter2": "*", - "async": "*", - "socket.io": "~0.9.8" + "express": "~2.5.9", + "everyauth": "~0.2.32", + "log": "~1.3.0", + "mongodb": "~1.1.2", + "node-promise": "~0.5.3", + "eventemitter2": "~0.4.9", + "async": "~0.1.22", + "socket.io": "~0.9.9", + "jade": "~0.27.0" }, "devDependencies": { "tobi": "*", "chai": "*", - "should": "*", "socket.io-client": "~0.9.8", - "mocha": "*" + "mocha": "*", + "jquery": "~1.7.3" }, "_id": "@0.0.1", "engines": { diff --git a/test/access.coffee b/test/access.coffee deleted file mode 100644 index 66ed97d..0000000 --- a/test/access.coffee +++ /dev/null @@ -1,54 +0,0 @@ -should = require('chai').should() -expect = require('chai').expect() -tobi = require 'tobi' - -describe('Access', ()-> - - app = {} - browser = {} - users = [] - groups = [] - tokens = [] - before((done)-> - app = require('./app')() - tobi.Browser.browsers = {} - browser = tobi.createBrowser(3001, 'local.host') - browser.userAgent = 'Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.100 Safari/534.30' - app.auth.addUser('local','ombr0',{},(err,userId)-> - users.push(userId) - console.log "IDI :" - console.log userId - app.token.add(userId,{},(err,token)-> - tokens.push(token) - console.log "I have a token :-D Hope I'm root ?" - done() - ) - ) - ) - - after(()-> - app.express.close() - ) - - describe('Root user', ()-> - it('First user should be root',(done)-> - console.log tokens[0] - app.access.check(tokens[0], ['_root'],(err,userId)-> - userId.should.eql(users[0]) - done() - ,'test/root') - ) - it('Second user should not be root',(done)-> - app.auth.addUser('local','ombr1',{},(err,userId)-> - users.push(userId) - app.token.add(userId,{},(err,token)-> - tokens.push(token) - app.access.check(token, ['_root'],(err,userId)-> - err[0].should.eql('Access denied') - done() - ,'test/root') - ) - ) - ) - ) -) diff --git a/test/access/root.coffee b/test/access/root.coffee new file mode 100644 index 0000000..87e9d07 --- /dev/null +++ b/test/access/root.coffee @@ -0,0 +1,59 @@ +should = require('chai').should() +expect = require('chai').expect() +tobi = require 'tobi' + +describe('Root Capabilities', ()-> + + + app = {} + tool = require '../tool' + + before(()-> + app = tool.app() + ) + + after(()-> + app.express.close() + ) + + rootToken = null + rootId = null + it('First user should be root',(done)-> + tool.user(app, (userId)-> + rootiId = userId + tool.token(app,userId, (token)-> + rootToken = token + setTimeout(()-> #Timeout required for event propagation + app.access.check(token, ['_root'],(err,rootId)-> + rootId.should.eql(userId) + done() + ,'test/root') + ,5) + ) + ) + ) + it('Second user should not be root',(done)-> + tool.user(app, (userId)-> + tool.token(app,userId, (token)-> + setTimeout(()-> #Timeout required for event propagation + app.access.check(token, ['_root'],(err,rootId)-> + should.exist(err) + should.exist(err[0]) + err[0].should.eql('Access denied') + done() + ,'test/root') + ,5) + ) + ) + ) + it('Should be able to create a group and put himself in it',(done)-> + groupName = tool._uniq('group') + app.group.add(groupName,rootToken,(err,groupId)-> + app.group.addUserToGroup(rootId,groupName,rootToken,(err,res)-> + res.should.be.true + should.not.exist(err) + done() + ) + ) + ) +) diff --git a/test/app.coffee b/test/app.coffee deleted file mode 100644 index d8851e7..0000000 --- a/test/app.coffee +++ /dev/null @@ -1,33 +0,0 @@ -module.exports = ()-> - Async = require 'async' - Log = require 'log' - - log = new Log() - configs = require '../configs-test' - - express = require 'express' - app = express.createServer( - express.bodyParser(), - express.static(__dirname + "/public"), - express.favicon(), - express.cookieParser(), - express.session({ secret: 'supersecret'}), - ) - app.log = log - - Auth = require '../lib/app' - auth = new Auth(app,configs) - - app.get('/', (req, res)-> - res.render('index.jade') - ) - - app.configure(()-> - app.set('view engine', 'jade') - app.set('views', __dirname + '/../views') - ) - - app.listen(configs.app.port) - log.info __dirname - log.info 'Application started http://local.host:'+configs.app.port - return auth diff --git a/configs-test.coffee b/test/configs.coffee similarity index 97% rename from configs-test.coffee rename to test/configs.coffee index 68effc3..e54e476 100644 --- a/configs-test.coffee +++ b/test/configs.coffee @@ -6,7 +6,9 @@ module.exports = configs ### configs.app = port : process.env.VCAP_APP_PORT or 3001 - +Log = require 'log' +configs.logger = new Log('error') +#configs.logger = new Log() ### #MONGO ### diff --git a/test/token/infos.coffee b/test/token/infos.coffee new file mode 100644 index 0000000..7a3b2d5 --- /dev/null +++ b/test/token/infos.coffee @@ -0,0 +1,39 @@ +should = require('chai').should() +expect = require('chai').expect() +tobi = require 'tobi' + +describe('Token GetInfo', ()-> + + + app = {} + tool = require '../tool' + + before(()-> + app = tool.app() + ) + + after(()-> + app.express.close() + ) + + rootToken = null + rootId = null + it('Root user should be able to get Info',(done)-> + tool.user(app, (userId)-> + rootId = userId + tool.token(app,userId, (token)-> + rootToken = token + setTimeout(()-> #Timeout required for event propagation + app.token.getInfo(token,(err,infos)-> + should.not.exist(err) + should.exist(infos) + infos.id.should.eql rootId + should.exist(infos.groups) + infos.groups.should.contain "_root" + done() + ) + ,5) + ) + ) + ) +) diff --git a/test/tool.coffee b/test/tool.coffee new file mode 100644 index 0000000..a924b97 --- /dev/null +++ b/test/tool.coffee @@ -0,0 +1,61 @@ +class Tool + + app:()-> + configs = require './configs' + + express = require 'express' + app = express.createServer( + express.bodyParser(), + express.static(__dirname + "/public"), + express.favicon(), + express.cookieParser(), + express.session({ secret: 'supersecret'}), + ) + + Auth = require '../lib/app' + auth = new Auth(app,configs) + + app.get('/', (req, res)-> + res.render('index.jade') + ) + + app.configure(()-> + app.set('view engine', 'jade') + app.set('views', __dirname + '/../views') + ) + app.listen(configs.app.port) + return auth + #Will Return the user or a new user + user : (app, cb, name)-> + if not name? + name = @_uniq('user') + app.auth.addUser('local',name,{},(err,userId)-> + if err != null + throw "An Error occured" + cb(userId) + ) + group: (app, cb, name)-> + if not name? + name = @_uniq('user') + app.stores.group.findByName(name,(err,group)-> + if err != null + app.stores.group.add(name, (err,groupId)-> + cb(groupId) + ) + return + cb(group.id) + ) + + token : (app,userId,cb)-> + app.token.add(userId,{}, (err,token)-> + if err != null + throw "An Error occured" + cb(token) + ) + + _uniq:(prefix = '')-> + timestamp = new Date().getTime() + id = Math.round( timestamp+""+Math.round(Math.random()*1000)) + return prefix+id + +module.exports = new Tool()