From a6b08ae7c4f4ffb21ee22691a913c02dc8187188 Mon Sep 17 00:00:00 2001 From: Tobe O Date: Sun, 26 Aug 2018 19:11:37 -0400 Subject: [PATCH] Bump to 2.0.0-alpha --- CHANGELOG.md | 6 +++ lib/src/middleware/require_auth.dart | 15 +++---- lib/src/plugin.dart | 63 +++++++++++----------------- lib/src/popup_page.dart | 4 +- lib/src/strategies/local.dart | 14 +++---- pubspec.yaml | 9 ++-- test/callback_test.dart | 24 ++++++----- test/local_test.dart | 18 ++++---- 8 files changed, 74 insertions(+), 79 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b384a7e1..fcbb8d5b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +# 2.0.0-alpha +* Depend on Dart 2 and Angel 2. +* Remove `dart2_constant`. +* Remove `requireAuth`. +* Remove `userKey`, instead favoring generic parameters. + # 1.2.0 * Deprecate `requireAuth`, in favor of `requireAuthentication`. * Allow configuring of the `userKey`. diff --git a/lib/src/middleware/require_auth.dart b/lib/src/middleware/require_auth.dart index 20c66645..bf286e95 100644 --- a/lib/src/middleware/require_auth.dart +++ b/lib/src/middleware/require_auth.dart @@ -3,24 +3,19 @@ import 'package:angel_framework/angel_framework.dart'; /// Forces Basic authentication over the requested resource, with the given [realm] name, if no JWT is present. /// /// [realm] defaults to `'angel_auth'`. -RequestHandler forceBasicAuth({String realm, String userKey: 'user'}) { +RequestHandler forceBasicAuth({String realm}) { return (RequestContext req, ResponseContext res) async { - if (req.properties.containsKey(userKey)) return true; + if (req.container.has()) return true; res ..statusCode = 401 ..headers['www-authenticate'] = 'Basic realm="${realm ?? 'angel_auth'}"' - ..end(); - return false; + ..close(); }; } -/// Use [requireAuthentication] instead. -@deprecated -final RequestMiddleware requireAuth = requireAuthentication(userKey: 'user'); - /// Restricts access to a resource via authentication. -RequestMiddleware requireAuthentication({String userKey: 'user'}) { +RequestHandler requireAuthentication() { return (RequestContext req, ResponseContext res, {bool throwError: true}) async { bool _reject(ResponseContext res) { @@ -31,7 +26,7 @@ RequestMiddleware requireAuthentication({String userKey: 'user'}) { return false; } - if (req.properties.containsKey(userKey) || req.method == 'OPTIONS') + if (req.container.has() || req.method == 'OPTIONS') return true; else return _reject(res); diff --git a/lib/src/plugin.dart b/lib/src/plugin.dart index 0e10799b..61abae1f 100644 --- a/lib/src/plugin.dart +++ b/lib/src/plugin.dart @@ -3,7 +3,6 @@ import 'dart:io'; import 'dart:math' as Math; import 'package:angel_framework/angel_framework.dart'; import 'package:crypto/crypto.dart'; -import 'middleware/require_auth.dart'; import 'auth_token.dart'; import 'defs.dart'; import 'options.dart'; @@ -38,15 +37,6 @@ class AngelAuth { /// Only applies if [allowCookie] is `true`. final String cookiePath; - /// The name to register [requireAuthentication] as. Default: `auth`. - @deprecated - String middlewareName; - - /// The name to inject authenticated users as. - /// - /// Defaults to `'user'`. - final String userKey; - /// If `true` (default), then JWT's will be considered invalid if used from a different IP than the first user's it was issued to. /// /// This is a security provision. Even if a user's JWT is stolen, a remote attacker will not be able to impersonate anyone. @@ -90,10 +80,8 @@ class AngelAuth { this.allowTokenInQuery: true, this.enforceIp: true, this.cookieDomain, - this.userKey: 'user', this.cookiePath: '/', this.secureCookies: true, - this.middlewareName: 'auth', this.reviveTokenEndpoint: "/auth/token"}) : super() { _hs256 = new Hmac(sha256, (jwtKey ?? _randomString()).codeUnits); @@ -108,11 +96,9 @@ class AngelAuth { throw new StateError( 'An `AngelAuth` plug-in was called without its `deserializer` being set. All authentication will fail.'); - app.container.singleton(this); - if (runtimeType != AngelAuth) app.container.singleton(this, as: AngelAuth); - - // ignore: deprecated_member_use - app.registerMiddleware(middlewareName, requireAuthentication()); + app.container.registerSingleton(this); + if (runtimeType != AngelAuth) + app.container.registerSingleton(this, as: AngelAuth); if (reviveTokenEndpoint != null) { app.post(reviveTokenEndpoint, reviveJwt); @@ -123,10 +109,11 @@ class AngelAuth { }); } - void _apply(RequestContext req, ResponseContext res, AuthToken token, user) { - req - ..inject(AuthToken, req.properties['token'] = token) - ..inject(user.runtimeType, req.properties[userKey] = user); + void _apply( + RequestContext req, ResponseContext res, AuthToken token, T user) { + req.container + ..registerSingleton(token) + ..registerSingleton(user); if (allowCookie == true) { _addProtectedCookie(res, 'token', token.serialize(_hs256)); @@ -176,8 +163,9 @@ class AngelAuth { } else if (allowCookie && req.cookies.any((cookie) => cookie.name == "token")) { return req.cookies.firstWhere((cookie) => cookie.name == "token").value; - } else if (allowTokenInQuery && req.query['token'] is String) { - return req.query['token']?.toString(); + } else if (allowTokenInQuery && + req.uri.queryParameters['token'] is String) { + return req.uri.queryParameters['token']?.toString(); } return null; @@ -214,7 +202,7 @@ class AngelAuth { var jwt = getJwt(req); if (jwt == null) { - var body = await req.lazyBody(); + var body = await req.parseBody(); jwt = body['token']?.toString(); } if (jwt == null) { @@ -282,14 +270,14 @@ class AngelAuth { orElse: () => throw new ArgumentError('No strategy "$name" found.')); - var hasExisting = req.properties.containsKey(userKey); + var hasExisting = req.container.has(); var result = hasExisting - ? req.properties[userKey] - : await strategy.authenticate(req, res, options); + ? req.container.make() + : await strategy.authenticate(req, res, options) as T; if (result == true) return result; else if (result != false) { - var userId = await serializer(result as T); + var userId = await serializer(result); // Create JWT var token = new AuthToken( @@ -297,8 +285,8 @@ class AngelAuth { var jwt = token.serialize(_hs256); if (options?.tokenCallback != null) { - var r = await options.tokenCallback( - req, res, token, req.properties[userKey] = result); + req.container.registerSingleton(result); + var r = await options.tokenCallback(req, res, token, result); if (r != null) return r; jwt = token.serialize(_hs256); } @@ -319,8 +307,8 @@ class AngelAuth { } else if (options?.canRespondWithJson != false && req.accepts('application/json')) { var user = hasExisting - ? result as T - : await deserializer(await serializer(result as T)); + ? result + : await deserializer(await serializer(result)); _onLogin.add(user); return {"data": user, "token": jwt}; } @@ -365,7 +353,7 @@ class AngelAuth { } /// Log an authenticated user out. - RequestMiddleware logout([AngelAuthOptions options]) { + RequestHandler logout([AngelAuthOptions options]) { return (RequestContext req, ResponseContext res) async { for (AuthStrategy strategy in strategies) { if (!(await strategy.canLogout(req, res))) { @@ -379,11 +367,10 @@ class AngelAuth { } } - var user = req.grab(userKey); - if (user != null) _onLogout.add(user as T); - - req.injections..remove(AuthToken)..remove(userKey); - req.properties.remove(userKey); + if (req.container.has()) { + var user = req.container.make(); + _onLogout.add(user); + } if (allowCookie == true) { res.cookies.removeWhere((cookie) => cookie.name == "token"); diff --git a/lib/src/popup_page.dart b/lib/src/popup_page.dart index 71a709ab..45a92b14 100644 --- a/lib/src/popup_page.dart +++ b/lib/src/popup_page.dart @@ -1,12 +1,12 @@ -import 'dart:io'; import 'package:angel_framework/angel_framework.dart'; +import 'package:http_parser/http_parser.dart'; import 'options.dart'; /// Displays a default callback page to confirm authentication via popups. AngelAuthCallback confirmPopupAuthentication({String eventName: 'token'}) { return (req, ResponseContext res, String jwt) async { res - ..contentType = new ContentType('text', 'html') + ..contentType = new MediaType('text', 'html') ..write(''' diff --git a/lib/src/strategies/local.dart b/lib/src/strategies/local.dart index 126d468b..c8858f8d 100644 --- a/lib/src/strategies/local.dart +++ b/lib/src/strategies/local.dart @@ -61,7 +61,7 @@ class LocalAuthStrategy extends AuthStrategy { res ..statusCode = 401 ..headers['www-authenticate'] = 'Basic realm="$realm"' - ..end(); + ..close(); return false; } @@ -70,11 +70,11 @@ class LocalAuthStrategy extends AuthStrategy { } if (verificationResult == null) { - await req.parse(); - if (_validateString(req.body[usernameField]?.toString()) && - _validateString(req.body[passwordField]?.toString())) { - verificationResult = await verifier(req.body[usernameField]?.toString(), - req.body[passwordField]?.toString()); + var body = await req.parseBody(); + if (_validateString(body[usernameField]?.toString()) && + _validateString(body[passwordField]?.toString())) { + verificationResult = await verifier( + body[usernameField]?.toString(), body[passwordField]?.toString()); } } @@ -89,7 +89,7 @@ class LocalAuthStrategy extends AuthStrategy { res ..statusCode = 401 ..headers['www-authenticate'] = 'Basic realm="$realm"' - ..end(); + ..close(); } return false; diff --git a/pubspec.yaml b/pubspec.yaml index 6a5348b6..721c23ab 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,16 +1,15 @@ name: angel_auth description: A complete authentication plugin for Angel. -version: 1.2.0 +version: 2.0.0-alpha author: Tobe O homepage: https://github.com/angel-dart/angel_auth environment: - sdk: ">=1.8.0 <3.0.0" + sdk: ">=2.0.0-dev <3.0.0" dependencies: - angel_framework: ^1.1.0-alpha + angel_framework: ^2.0.0-alpha crypto: ^2.0.0 - dart2_constant: ^1.0.0 dev_dependencies: http: ^0.11.0 io: ^0.3.2 logging: ^0.11.0 - test: ^0.12.0 + test: ^1.0.0 diff --git a/test/callback_test.dart b/test/callback_test.dart index c1bdf3c2..c74c0191 100644 --- a/test/callback_test.dart +++ b/test/callback_test.dart @@ -1,7 +1,6 @@ import 'dart:io'; import 'package:angel_auth/angel_auth.dart'; import 'package:angel_framework/angel_framework.dart'; -import 'package:angel_framework/common.dart'; import 'package:dart2_constant/convert.dart'; import 'package:http/http.dart' as http; import 'package:io/ansi.dart'; @@ -17,7 +16,7 @@ class User extends Model { main() { Angel app; AngelHttp angelHttp; - AngelAuth auth; + AngelAuth auth; http.Client client; HttpServer server; String url; @@ -26,7 +25,7 @@ main() { hierarchicalLoggingEnabled = true; app = new Angel(); angelHttp = new AngelHttp(app); - app.use('/users', new TypedService(new MapService())); + app.use('/users', new MapService()); var oldErrorHandler = app.errorHandler; app.errorHandler = (e, req, res) { @@ -58,7 +57,7 @@ main() { (id) async => await app.service('users').read(id) as User; await app.configure(auth.configureServer); - app.use(auth.decodeJwt); + app.fallback(auth.decodeJwt); auth.strategies.add(new LocalAuthStrategy((username, password) async { final List users = await app.service('users').index(); @@ -75,14 +74,19 @@ main() { new AngelAuthOptions(callback: (req, res, token) { res ..write('Hello!') - ..end(); + ..close(); }))); - app.chain((RequestContext req) { - req.properties['user'] = - new User(username: req.params['name']?.toString()); - return true; - }).post('/existing/:name', auth.authenticate('local')); + app.chain([ + (req, res) { + req.container.registerSingleton( + new User(username: req.params['name']?.toString())); + return true; + } + ]).post( + '/existing/:name', + auth.authenticate('local'), + ); client = new http.Client(); server = await angelHttp.startServer(); diff --git a/test/local_test.dart b/test/local_test.dart index e6b2ee28..2eb8a5a6 100644 --- a/test/local_test.dart +++ b/test/local_test.dart @@ -6,11 +6,12 @@ import 'package:dart2_constant/convert.dart'; import 'package:http/http.dart' as http; import 'package:test/test.dart'; -final AngelAuth auth = new AngelAuth(); +final AngelAuth> auth = + new AngelAuth>(); var headers = {'accept': 'application/json'}; AngelAuthOptions localOpts = new AngelAuthOptions( failureRedirect: '/failure', successRedirect: '/success'); -Map sampleUser = {'hello': 'world'}; +Map sampleUser = {'hello': 'world'}; Future verifier(String username, String password) async { if (username == 'username' && password == 'password') { @@ -25,7 +26,7 @@ Future wireAuth(Angel app) async { auth.strategies.add(new LocalAuthStrategy(verifier)); await app.configure(auth.configureServer); - app.use(auth.decodeJwt); + app.fallback(auth.decodeJwt); } main() async { @@ -40,11 +41,14 @@ main() async { app = new Angel(); angelHttp = new AngelHttp(app, useZone: false); await app.configure(wireAuth); - app.get('/hello', 'Woo auth', middleware: [auth.authenticate('local')]); - app.post('/login', 'This should not be shown', + app.get('/hello', (req, res) => 'Woo auth', + middleware: [auth.authenticate('local')]); + app.post('/login', (req, res) => 'This should not be shown', middleware: [auth.authenticate('local', localOpts)]); - app.get('/success', "yep", middleware: ['auth']); - app.get('/failure', "nope"); + app.get('/success', (req, res) => "yep", middleware: [ + requireAuthentication>(), + ]); + app.get('/failure', (req, res) => "nope"); HttpServer server = await angelHttp.startServer('127.0.0.1', 0); url = "http://${server.address.host}:${server.port}";