diff --git a/Makefile b/Makefile index 50ca797..35f4306 100644 --- a/Makefile +++ b/Makefile @@ -17,6 +17,7 @@ test: --reporter $(REPORTER) \ --timeout $(TIMEOUT) \ --require should \ + --require should-http \ $(MOCHA_OPTS) \ $(TESTS) @@ -27,6 +28,7 @@ test-cov: --reporter $(REPORTER) \ --timeout $(TIMEOUT) \ --require should \ + --require should-http \ $(MOCHA_OPTS) \ $(TESTS) @@ -38,6 +40,7 @@ test-travis: --reporter $(REPORTER) \ --timeout $(TIMEOUT) \ --require should \ + --require should-http \ $(MOCHA_OPTS) \ $(TESTS) diff --git a/README.md b/README.md index 6bbda1b..a090080 100644 --- a/README.md +++ b/README.md @@ -60,12 +60,16 @@ app.use(userauth({ ### Arguments +If `options.match` or `options.ignore` is `String` instance, +we will use [path-match](https://github.com/expressjs/path-match) transfer it to `Regex` instance. + ```js /** * User auth middleware. * * @param {Object} [options] * - {String|Regex|Function(pathname, ctx)} match, detect which url need to check user auth. + * `''` empty string meaning match all, @see `path-match` package. * - {String|Regex|Function(pathname, ctx)} ignore, detect which url no need to check user auth. * If `match` exists, this argument will be ignored. * - {Function(url, rootPath)} loginURLForamter, format the login url. diff --git a/index.js b/index.js index 49b2d37..da7c99a 100644 --- a/index.js +++ b/index.js @@ -1,7 +1,12 @@ -/*! +/**! * koa-userauth - index.js - * Copyright(c) 2014 dead_horse + * + * Copyright(c) koajs and other contributors. * MIT Licensed + * + * Authors: + * dead_horse + * fengmk2 (http://fengmk2.github.com) */ 'use strict'; @@ -32,7 +37,7 @@ var defaultOptions = { * User auth middleware. * * @param {String|Regex|Function(pathname, ctx)} match, detect which url need to check user auth. - * @param {Object} [options] + * @param {Object} options * - {Function(url, rootPath)} loginURLForamter, format the login url. * - {String} [rootPath], custom app url root path, default is '/'. * - {String} [loginPath], default is '/login'. @@ -47,13 +52,13 @@ var defaultOptions = { * @public */ -module.exports = function (match, options) { - // userauth(options) - if (arguments.length === 1) { - options = match; - match = null; +module.exports = function (options) { + // userauth(match, options) + if (arguments.length === 2) { + options = arguments[1]; + options.match = arguments[0]; } - options = options || {}; + copy(defaultOptions).to(options); options.loginCallbackPath = options.loginCallbackPath @@ -69,7 +74,7 @@ module.exports = function (match, options) { options.getUser = options.getUser; options.redirectHandler = options.redirectHandler || defaultRedirectHandler; - match = options.match || match; + var match = options.match; var ignore = options.ignore; // need login checker @@ -99,6 +104,7 @@ module.exports = function (match, options) { } else { // ignore all needLogin = function () {}; + debug('ignore all paths'); } } @@ -123,12 +129,23 @@ module.exports = function (match, options) { */ return function* userauth(next) { - - debug('url: %s, loginPath: %s, session exists: %s', - this.url, options.loginPath, !!this.session); + var loginRequired = !!needLogin(this.path); + debug('url: %s, path: %s, loginPath: %s, session exists: %s, login required: %s', + this.url, this.path, options.loginPath, !!this.session, loginRequired); + + if (!this.session) { + debug('this.session not exists'); + // ignore not match path + if (!loginRequired) { + debug('not match needLogin path, %j', this.path); + return yield* next; + } + debug('relogin again'); + return yield* loginHandler.call(this, next); + } // get login path - if (!this.session || this.path === options.loginPath) { + if (this.path === options.loginPath) { debug('match login path'); return yield* loginHandler.call(this, next); } @@ -146,15 +163,15 @@ module.exports = function (match, options) { } // ignore not match path - if (!needLogin(this.path)) { - debug('not match needLogin path, %j', needLogin(this.path)); + if (!loginRequired) { + debug('ignore %j', this.path); return yield* next; } if (this.session[options.userField] && options.loginCheck(this)) { // 4. user logined, next() handler - debug('already login'); + debug('already logined'); return yield* next; } @@ -163,8 +180,9 @@ module.exports = function (match, options) { try { user = yield* options.getUser(this); } catch (err) { - console.error(err.stack); + console.error('[koa-userauth] options.getUser error: %s', err.stack); } + if (!user) { debug('can not get user'); var ctx = this; @@ -186,6 +204,7 @@ module.exports = function (match, options) { return yield* options.redirectHandler.call(this, nextHandler); } + debug('get user directly'); var res = yield* options.loginCallback(this, user); var loginUser = res[0]; diff --git a/package.json b/package.json index e0cea99..08335c5 100644 --- a/package.json +++ b/package.json @@ -29,19 +29,20 @@ "autod": "^0.1.3", "istanbul-harmony": "*", "jshint": "*", - "koa": "~0.10.0", + "koa": "~0.12.1", + "koa-generic-session": "~1.2.2", "koa-route": "~2.1.0", - "koa-generic-session": "~1.0.0", "mm": "~0.2.1", "mocha": "*", "pedding": "~1.0.0", - "should": "~3.3.2", + "should": "~4.0.4", + "should-http": "*", "supertest": "~0.13.0" }, "dependencies": { "copy-to": "~1.0.1", "debug": "~2.0.0", "is-type-of": "~0.3.1", - "path-match": "~1.2.0" + "path-match": "~1.2.2" } } diff --git a/test/koa-userauth.test.js b/test/index.test.js similarity index 73% rename from test/koa-userauth.test.js rename to test/index.test.js index 91c564d..cf809a8 100644 --- a/test/koa-userauth.test.js +++ b/test/index.test.js @@ -1,7 +1,12 @@ -/*! - * koa-userauth - index.js - * Copyright(c) 2014 dead_horse +/**! + * koa-userauth - index.test.js + * + * Copyright(c) koajs and other contributors. * MIT Licensed + * + * Authors: + * dead_horse + * fengmk2 (http://fengmk2.github.com) */ 'use strict'; @@ -12,17 +17,157 @@ var pedding = require('pedding'); var mm = require('mm'); -var userauth = require('../'); var should = require('should'); var request = require('supertest'); +var koa = require('koa'); +var session = require('koa-generic-session'); +var userauth = require('../'); var createApp = require('./app'); function match(path) { return path.indexOf('/user') === 0; } +describe('index.test.js', function () { + describe('userauth([match, ]options)', function () { + it('should support match="" to match all', function (done) { + var app = koa(); + app.keys = ['i m secret']; + app.use(session()); + app.use(userauth('', { + loginURLForamter: function (url) { + return 'http://auth.example.com/login'; + }, + getUser: function* () { + return null; + } + })); + request(app.listen()) + .get('/') + .expect('Location', '/login?redirect=%2F') + .expect(302, done); + }); + + it('should support options.match="" to match all', function (done) { + var app = koa(); + app.keys = ['i m secret']; + app.use(session()); + app.use(userauth({ + match: '', + loginURLForamter: function (url) { + return 'http://auth.example.com/login'; + }, + getUser: function* () { + return null; + } + })); + request(app.listen()) + .get('/') + .expect('Location', '/login?redirect=%2F') + .expect(302, done); + }); + + it('should support options.match=null to not match all', function (done) { + var app = koa(); + app.keys = ['i m secret']; + app.use(session()); + app.use(userauth({ + match: null, + loginURLForamter: function (url) { + return 'http://auth.example.com/login'; + }, + getUser: function* () { + return null; + } + })); + app.use(function* () { + this.body = 'pass'; + }); + request(app.listen()) + .get('/') + .expect('pass') + .expect(200, done); + }); + + it('should GET /login redirect to login url', function (done) { + var app = koa(); + app.keys = ['i m secret']; + app.use(session()); + app.use(userauth({ + match: '', + loginURLForamter: function (url) { + return 'http://auth.example.com/login'; + }, + getUser: function* () { + return null; + } + })); + request(app.listen()) + .get('/login') + .expect('Location', 'http://auth.example.com/login') + .expect(302, done); + }); + + it('should support rootPath=/foo', function (done) { + var app = koa(); + app.keys = ['i m secret']; + app.use(session()); + app.use(userauth({ + match: '', + rootPath: '/foo', + loginURLForamter: function (url) { + return 'http://auth.example.com/login'; + }, + getUser: function* () { + return null; + } + })); + request(app.listen()) + .get('/foo/login') + .expect('Location', 'http://auth.example.com/login') + .expect(302, done); + }); + + it('should always redirect to login page when session not exists', function (done) { + var app = koa(); + app.keys = ['i m secret']; + app.use(userauth({ + match: '/user', + loginURLForamter: function (url) { + return 'http://auth.example.com/login'; + }, + getUser: function* () { + return null; + } + })); + request(app.listen()) + .get('/user') + .expect('Location', 'http://auth.example.com/login') + .expect(302, done); + }); + + it('should pass when session not exists and not match need login', function (done) { + var app = koa(); + app.keys = ['i m secret']; + app.use(userauth({ + match: '/user', + loginURLForamter: function (url) { + return 'http://auth.example.com/login'; + }, + getUser: function* () { + return null; + } + })); + app.use(function* () { + this.body = 'pass'; + }); + request(app.listen()) + .get('/') + .expect('pass') + .expect(200, done); + }); + }); -describe('userauth.test.js', function () { [ ['string', '/user'], ['regexp', /^\/user/], @@ -41,9 +186,9 @@ describe('userauth.test.js', function () { request(app) .get('/login') - .expect('Location', /^\/mocklogin\?redirect\=/) + .expect('Location', /^\/mocklogin\?redirect/) .expect('Location', /\/login\/callback$/) - .expect('Set-Cookie', /^koa\.sid\=/) + .expect('Set-Cookie', /^koa\.sid/) .expect(302, function (err, res) { should.not.exist(err); var cookie = res.headers['set-cookie'].join(';'); @@ -58,21 +203,21 @@ describe('userauth.test.js', function () { request(app) .get('/login?foo=bar') - .expect('Location', /^\/mocklogin\?redirect\=/) + .expect('Location', /^\/mocklogin\?redirect/) .expect('Location', /\/login\/callback$/) .expect(302, done); request(app) .get('/login?foo=bar') .set('Accept', 'application/json') - .expect('Location', /^\/mocklogin\?redirect\=/) + .expect('Location', /^\/mocklogin\?redirect/) .expect({ error: '401 Unauthorized' }) .expect(401, done); request(app) .get('/login?redirect=user/index') - .expect('Location', /^\/mocklogin\?redirect\=/) - .expect('Set-Cookie', /^koa\.sid\=/) + .expect('Location', /^\/mocklogin\?redirect/) + .expect('Set-Cookie', /^koa\.sid/) .expect(302, function (err, res) { should.not.exist(err); var cookie = res.headers['set-cookie'].join(';'); @@ -86,8 +231,8 @@ describe('userauth.test.js', function () { request(app) .get('/login?redirect=/index2') - .expect('Location', /^\/mocklogin\?redirect\=/) - .expect('Set-Cookie', /^koa\.sid\=/) + .expect('Location', /^\/mocklogin\?redirect/) + .expect('Set-Cookie', /^koa\.sid/) .expect(302, function (err, res) { should.not.exist(err); var cookie = res.headers['set-cookie'].join(';'); @@ -105,7 +250,7 @@ describe('userauth.test.js', function () { .get('/login/callback') .set('mocklogin', 1) .expect('Location', '/') - .expect('Set-Cookie', /^koa\.sid\=/) + .expect('Set-Cookie', /^koa\.sid/) .expect(302, function (err, res) { var cookie = res.headers['set-cookie'].join(';'); request(app) @@ -122,8 +267,8 @@ describe('userauth.test.js', function () { request(app) .get('/login?redirect=') .set('Referer', 'http://demo.com/foo') - .expect('Location', /^\/mocklogin\?redirect\=/) - .expect('Set-Cookie', /^koa\.sid\=/) + .expect('Location', /^\/mocklogin\?redirect/) + .expect('Set-Cookie', /^koa\.sid/) .expect(302, function (err, res) { should.not.exist(err); var cookie = res.headers['set-cookie'].join(';'); @@ -139,8 +284,8 @@ describe('userauth.test.js', function () { request(app) .get('/login') .set('Referer', 'foo/bar') - .expect('Location', /^\/mocklogin\?redirect\=/) - .expect('Set-Cookie', /^koa\.sid\=/) + .expect('Location', /^\/mocklogin\?redirect/) + .expect('Set-Cookie', /^koa\.sid/) .expect(302, function (err, res) { should.not.exist(err); var cookie = res.headers['set-cookie'].join(';'); @@ -156,8 +301,8 @@ describe('userauth.test.js', function () { request(app) .get('/login') .set('Referer', '/foo/bar') - .expect('Location', /^\/mocklogin\?redirect\=/) - .expect('Set-Cookie', /^koa\.sid\=/) + .expect('Location', /^\/mocklogin\?redirect/) + .expect('Set-Cookie', /^koa\.sid/) .expect(302, function (err, res) { should.not.exist(err); var cookie = res.headers['set-cookie'].join(';'); @@ -173,8 +318,8 @@ describe('userauth.test.js', function () { request(app) .get('/login') .set('Referer', '/login') - .expect('Location', /^\/mocklogin\?redirect\=/) - .expect('Set-Cookie', /^koa\.sid\=/) + .expect('Location', /^\/mocklogin\?redirect/) + .expect('Set-Cookie', /^koa\.sid/) .expect(302, function (err, res) { should.not.exist(err); var cookie = res.headers['set-cookie'].join(';');