Updated auth
This commit is contained in:
parent
7652c02183
commit
7cbbc686ba
31 changed files with 182 additions and 146 deletions
|
@ -1,5 +1,11 @@
|
|||
# Change Log
|
||||
|
||||
## 4.1.1
|
||||
|
||||
* Changed `userId` field of `AuthToken` to String type
|
||||
* Changed `serializer` return value to String type
|
||||
* Changed `deserializer` input parameter to String type
|
||||
|
||||
## 4.1.0
|
||||
|
||||
* Updated linter to `package:lints`
|
||||
|
|
|
@ -18,9 +18,10 @@ Ensure you have read the [User Guide](https://angel3-docs.dukefirehawk.com/guide
|
|||
|
||||
```dart
|
||||
configureServer(Angel app) async {
|
||||
var auth = AngelAuth<User>();
|
||||
auth.serializer = ...;
|
||||
auth.deserializer = ...;
|
||||
var auth = AngelAuth<User>(
|
||||
serializer: (user) => user.id ?? '',
|
||||
deserializer: (id) => fetchAUserByIdSomehow(id)
|
||||
);
|
||||
auth.strategies['local'] = LocalAuthStrategy(...);
|
||||
|
||||
// POST route to handle username+password
|
||||
|
|
|
@ -6,7 +6,7 @@ import 'package:angel3_framework/http.dart';
|
|||
void main() async {
|
||||
var app = Angel();
|
||||
var auth = AngelAuth<User>(
|
||||
serializer: (user) => user.id,
|
||||
serializer: (user) => user.id ?? '',
|
||||
deserializer: (id) => fetchAUserByIdSomehow(id));
|
||||
|
||||
// Middleware to decode JWT's and inject a user object...
|
||||
|
@ -31,7 +31,7 @@ class User {
|
|||
String? id, username, password;
|
||||
}
|
||||
|
||||
Future<User> fetchAUserByIdSomehow(id) async {
|
||||
Future<User> fetchAUserByIdSomehow(String id) async {
|
||||
// Fetch a user somehow...
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
|
|
@ -32,41 +32,35 @@ class AuthToken {
|
|||
SplayTreeMap.from({'alg': 'HS256', 'typ': 'JWT'});
|
||||
|
||||
String? ipAddress;
|
||||
late DateTime issuedAt;
|
||||
num lifeSpan;
|
||||
dynamic userId;
|
||||
String userId;
|
||||
late DateTime issuedAt;
|
||||
Map<String, dynamic> payload = {};
|
||||
|
||||
AuthToken(
|
||||
{this.ipAddress,
|
||||
this.lifeSpan = -1,
|
||||
this.userId,
|
||||
required this.userId,
|
||||
DateTime? issuedAt,
|
||||
Map payload = const {}}) {
|
||||
Map<String, dynamic>? payload}) {
|
||||
this.issuedAt = issuedAt ?? DateTime.now();
|
||||
this.payload.addAll(payload.keys
|
||||
.fold({}, ((out, k) => out?..[k.toString()] = payload[k])) ??
|
||||
{});
|
||||
/*
|
||||
this.payload.addAll(payload.keys.fold(
|
||||
{},
|
||||
((out, k) => out..[k.toString()] = payload[k])
|
||||
as Map<String, dynamic>? Function(
|
||||
Map<String, dynamic>?, dynamic)) ??
|
||||
{});
|
||||
*/
|
||||
if (payload != null) {
|
||||
this.payload.addAll(payload.keys
|
||||
.fold({}, ((out, k) => out?..[k.toString()] = payload[k])) ??
|
||||
{});
|
||||
}
|
||||
}
|
||||
|
||||
factory AuthToken.fromJson(String jsons) =>
|
||||
AuthToken.fromMap(json.decode(jsons) as Map);
|
||||
AuthToken.fromMap(json.decode(jsons) as Map<String, dynamic>);
|
||||
|
||||
factory AuthToken.fromMap(Map data) {
|
||||
factory AuthToken.fromMap(Map<String, dynamic> data) {
|
||||
return AuthToken(
|
||||
ipAddress: data['aud'].toString(),
|
||||
lifeSpan: data['exp'] as num,
|
||||
issuedAt: DateTime.parse(data['iat'].toString()),
|
||||
userId: data['sub'],
|
||||
payload: data['pld'] as Map);
|
||||
payload: data['pld']);
|
||||
}
|
||||
|
||||
factory AuthToken.parse(String jwt) {
|
||||
|
@ -78,7 +72,8 @@ class AuthToken {
|
|||
}
|
||||
|
||||
var payloadString = decodeBase64(split[1]);
|
||||
return AuthToken.fromMap(json.decode(payloadString) as Map);
|
||||
return AuthToken.fromMap(
|
||||
json.decode(payloadString) as Map<String, dynamic>);
|
||||
}
|
||||
|
||||
factory AuthToken.validate(String jwt, Hmac hmac) {
|
||||
|
@ -100,7 +95,8 @@ class AuthToken {
|
|||
message: 'JWT payload does not match hashed version.');
|
||||
}
|
||||
|
||||
return AuthToken.fromMap(json.decode(payloadString) as Map);
|
||||
return AuthToken.fromMap(
|
||||
json.decode(payloadString) as Map<String, dynamic>);
|
||||
}
|
||||
|
||||
String serialize(Hmac hmac) {
|
||||
|
@ -111,7 +107,7 @@ class AuthToken {
|
|||
return data + '.' + base64Url.encode(signature);
|
||||
}
|
||||
|
||||
Map toJson() {
|
||||
Map<String, dynamic> toJson() {
|
||||
return _splayify({
|
||||
'iss': 'angel_auth',
|
||||
'aud': ipAddress,
|
||||
|
@ -123,7 +119,7 @@ class AuthToken {
|
|||
}
|
||||
}
|
||||
|
||||
SplayTreeMap _splayify(Map map) {
|
||||
Map<String, dynamic> _splayify(Map<String, dynamic> map) {
|
||||
var data = {};
|
||||
map.forEach((k, v) {
|
||||
data[k] = _splay(v);
|
||||
|
@ -131,11 +127,11 @@ SplayTreeMap _splayify(Map map) {
|
|||
return SplayTreeMap.from(data);
|
||||
}
|
||||
|
||||
dynamic _splay(value) {
|
||||
dynamic _splay(dynamic value) {
|
||||
if (value is Iterable) {
|
||||
return value.map(_splay).toList();
|
||||
} else if (value is Map) {
|
||||
return _splayify(value);
|
||||
return _splayify(value as Map<String, dynamic>);
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
|
|
|
@ -46,7 +46,7 @@ class ExternalAuthOptions {
|
|||
/// * `client_id`
|
||||
/// * `client_secret`
|
||||
/// * `redirect_uri`
|
||||
factory ExternalAuthOptions.fromMap(Map map) {
|
||||
factory ExternalAuthOptions.fromMap(Map<String, dynamic> map) {
|
||||
var clientId = map['client_id'];
|
||||
var clientSecret = map['client_secret'];
|
||||
if (clientId == null || clientSecret == null) {
|
||||
|
@ -55,8 +55,8 @@ class ExternalAuthOptions {
|
|||
}
|
||||
|
||||
return ExternalAuthOptions(
|
||||
clientId: clientId as String,
|
||||
clientSecret: clientSecret as String,
|
||||
clientId: clientId,
|
||||
clientSecret: clientSecret,
|
||||
redirectUri: map['redirect_uri'],
|
||||
scopes: map['scopes'] is Iterable
|
||||
? ((map['scopes'] as Iterable).map((x) => x.toString()))
|
||||
|
|
|
@ -52,10 +52,10 @@ class AngelAuth<User> {
|
|||
Map<String, AuthStrategy<User>> strategies = {};
|
||||
|
||||
/// Serializes a user into a unique identifier associated only with one identity.
|
||||
FutureOr Function(User) serializer;
|
||||
FutureOr<String> Function(User) serializer;
|
||||
|
||||
/// Deserializes a unique identifier into its associated identity. In most cases, this is a user object or model instance.
|
||||
FutureOr<User> Function(Object) deserializer;
|
||||
FutureOr<User> Function(String) deserializer;
|
||||
|
||||
/// Fires the result of [deserializer] whenever a user signs in to the application.
|
||||
Stream<User> get onLogin => _onLogin.stream;
|
||||
|
@ -200,6 +200,7 @@ class AngelAuth<User> {
|
|||
/// String getUsername(User user) => user.name
|
||||
/// }
|
||||
/// ```
|
||||
/*
|
||||
@deprecated
|
||||
Future decodeJwt(RequestContext req, ResponseContext res) async {
|
||||
if (req.method == 'POST' && req.path == reviveTokenEndpoint) {
|
||||
|
@ -209,6 +210,7 @@ class AngelAuth<User> {
|
|||
return true;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
Future<_AuthResult<User>?> _decodeJwt(
|
||||
RequestContext req, ResponseContext res) async {
|
||||
|
@ -431,7 +433,7 @@ class AngelAuth<User> {
|
|||
req.accepts('application/json')) {
|
||||
var user = hasExisting
|
||||
? result
|
||||
: await deserializer((await serializer(result)) as Object);
|
||||
: await deserializer(await serializer(result));
|
||||
_onLogin.add(user);
|
||||
return {'data': user, 'token': jwt};
|
||||
}
|
||||
|
@ -469,8 +471,8 @@ class AngelAuth<User> {
|
|||
|
||||
/// Log a user in on-demand.
|
||||
Future loginById(
|
||||
dynamic userId, RequestContext req, ResponseContext res) async {
|
||||
var user = await deserializer(userId as Object);
|
||||
String userId, RequestContext req, ResponseContext res) async {
|
||||
var user = await deserializer(userId);
|
||||
var token =
|
||||
AuthToken(userId: userId, lifeSpan: _jwtLifeSpan, ipAddress: req.ip);
|
||||
_apply(req, res, token, user);
|
||||
|
|
|
@ -38,8 +38,8 @@ class LocalAuthStrategy<User> extends AuthStrategy<User> {
|
|||
|
||||
@override
|
||||
Future<User?> authenticate(RequestContext req, ResponseContext res,
|
||||
[AngelAuthOptions? options_]) async {
|
||||
var options = options_ ?? AngelAuthOptions();
|
||||
[AngelAuthOptions? options]) async {
|
||||
var _options = options ?? AngelAuthOptions();
|
||||
User? verificationResult;
|
||||
|
||||
if (allowBasic) {
|
||||
|
@ -88,9 +88,9 @@ class LocalAuthStrategy<User> extends AuthStrategy<User> {
|
|||
|
||||
if (verificationResult == null ||
|
||||
(verificationResult is Map && verificationResult.isEmpty)) {
|
||||
if (options.failureRedirect != null &&
|
||||
options.failureRedirect!.isNotEmpty) {
|
||||
await res.redirect(options.failureRedirect, code: 401);
|
||||
if (_options.failureRedirect != null &&
|
||||
_options.failureRedirect!.isNotEmpty) {
|
||||
await res.redirect(_options.failureRedirect, code: 401);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
name: angel3_auth
|
||||
description: A complete authentication plugin for Angel3. Includes support for stateless JWT tokens, Basic Auth, and more.
|
||||
version: 4.1.0
|
||||
version: 4.1.1
|
||||
homepage: https://angel3-framework.web.app/
|
||||
repository: https://github.com/dukefirehawk/angel/tree/master/packages/auth
|
||||
environment:
|
||||
|
@ -21,5 +21,5 @@ dev_dependencies:
|
|||
test: ^1.17.4
|
||||
lints: ^1.0.0
|
||||
#dependency_overrides:
|
||||
# angel3_container:
|
||||
# path: ../container/angel_container
|
||||
# angel3_framework:
|
||||
# path: ../framework
|
||||
|
|
|
@ -73,7 +73,7 @@ void main() {
|
|||
?.create({'username': 'jdoe1', 'password': 'password'});
|
||||
|
||||
auth = AngelAuth<User>(
|
||||
serializer: (u) => u.id,
|
||||
serializer: (u) => u.id ?? '',
|
||||
deserializer: (id) async =>
|
||||
await app.findService('users')?.read(id) as User);
|
||||
//auth.serializer = (u) => u.id;
|
||||
|
|
|
@ -8,7 +8,7 @@ import 'package:logging/logging.dart';
|
|||
import 'package:test/test.dart';
|
||||
|
||||
final AngelAuth<Map<String, String>> auth = AngelAuth<Map<String, String>>(
|
||||
serializer: (user) async => 1337, deserializer: (id) async => sampleUser);
|
||||
serializer: (user) async => '1337', deserializer: (id) async => sampleUser);
|
||||
var headers = <String, String>{'accept': 'application/json'};
|
||||
var localOpts = AngelAuthOptions<Map<String, String>>(
|
||||
failureRedirect: '/failure', successRedirect: '/success');
|
||||
|
|
|
@ -11,7 +11,7 @@ void main() {
|
|||
secureCookies: true,
|
||||
cookieDomain: 'SECURE',
|
||||
jwtLifeSpan: threeDays.inMilliseconds,
|
||||
serializer: (u) => u,
|
||||
serializer: (u) => u as String,
|
||||
deserializer: (u) => u);
|
||||
|
||||
setUp(() => defaultCookie = Cookie('a', 'b'));
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
# Change Log
|
||||
|
||||
## 4.1.0
|
||||
|
||||
* Updated linter to `package:lints`
|
||||
|
||||
## 4.0.2
|
||||
|
||||
* Updated example
|
||||
|
|
|
@ -1,21 +1,29 @@
|
|||
MIT License (MIT)
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2021 dukefirehawk.com
|
||||
Copyright (c) 2021, dukefirehawk.com
|
||||
All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
# Angel3 OAuth2 Handler
|
||||
|
||||
[![version](https://img.shields.io/badge/pub-v4.0.2-brightgreen)](https://pub.dartlang.org/packages/angel3_auth_oauth2)
|
||||
[![Null Safety](https://img.shields.io/badge/null-safety-brightgreen)](https://dart.dev/null-safety)
|
||||
![Pub Version (including pre-releases)](https://img.shields.io/pub/v/angel3_auth_oauth2?include_prereleases)
|
||||
![Null Safety](https://img.shields.io/badge/null-safety-brightgreen)](<https://dart.dev/null-safety>)
|
||||
[![Gitter](https://img.shields.io/gitter/room/angel_dart/discussion)](https://gitter.im/angel_dart/discussion)
|
||||
|
||||
[![License](https://img.shields.io/github/license/dukefirehawk/angel)](https://github.com/dukefirehawk/angel/tree/angel3/packages/auth_oauth2/LICENSE)
|
||||
[![License](https://img.shields.io/github/license/dukefirehawk/angel)](https://github.com/dukefirehawk/angel/tree/master/packages/auth_oauth2/LICENSE)
|
||||
|
||||
Angel3 library for authenticating users with remote identity providers via OAuth2, i.e. Facebook, Google, Azure AD, etc.
|
||||
|
||||
|
|
|
@ -1,4 +1 @@
|
|||
include: package:pedantic/analysis_options.yaml
|
||||
analyzer:
|
||||
strong-mode:
|
||||
implicit-casts: false
|
||||
include: package:lints/recommended.yaml
|
|
@ -48,8 +48,8 @@ void main() async {
|
|||
|
||||
// Set up the authenticator plugin.
|
||||
var auth = AngelAuth<User>(
|
||||
serializer: (user) async => user.id,
|
||||
deserializer: (id) => mappedUserService.read(id.toString()),
|
||||
serializer: (user) async => user.id ?? '',
|
||||
deserializer: (id) => mappedUserService.read(id),
|
||||
jwtKey: 'oauth2 example secret',
|
||||
allowCookie: false);
|
||||
await app.configure(auth.configureServer);
|
||||
|
@ -120,7 +120,7 @@ class User extends Model {
|
|||
|
||||
User({this.id, this.githubId});
|
||||
|
||||
static User parse(Map map) =>
|
||||
static User parse(Map<String, dynamic> map) =>
|
||||
User(id: map['id'] as String?, githubId: map['github_id'] as int?);
|
||||
|
||||
static Map<String, dynamic> serialize(User user) => user.toJson();
|
||||
|
|
|
@ -1,15 +1,19 @@
|
|||
name: angel3_auth_oauth2
|
||||
version: 4.0.2
|
||||
version: 4.1.0
|
||||
description: Angel3 library for authenticating users with external identity providers via OAuth2.
|
||||
homepage: https://angel3-framework.web.app/
|
||||
repository: https://github.com/dukefirehawk/angel/tree/angel3/packages/auth_oauth2
|
||||
repository: https://github.com/dukefirehawk/angel/tree/master/packages/auth_oauth2
|
||||
environment:
|
||||
sdk: '>=2.12.0 <3.0.0'
|
||||
dependencies:
|
||||
angel3_auth: ^4.0.0
|
||||
angel3_framework: ^4.0.0
|
||||
angel3_auth: ^4.1.0
|
||||
angel3_framework: ^4.2.0
|
||||
http_parser: ^4.0.0
|
||||
oauth2: ^2.0.0
|
||||
dev_dependencies:
|
||||
logging: ^1.0.1
|
||||
pedantic: ^1.11.0
|
||||
lints: ^1.0.0
|
||||
#dependency_overrides:
|
||||
# angel3_auth:
|
||||
# path: ../auth
|
||||
|
|
@ -5,7 +5,7 @@ import 'dart:async';
|
|||
import 'package:collection/collection.dart';
|
||||
import 'dart:convert';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:logging/logging.dart';
|
||||
//import 'package:logging/logging.dart';
|
||||
export 'package:angel3_http_exception/angel3_http_exception.dart';
|
||||
|
||||
/// A function that configures an [Angel] client in some way.
|
||||
|
|
|
@ -25,6 +25,6 @@ dev_dependencies:
|
|||
test: ^1.17.5
|
||||
lints: ^1.0.0
|
||||
#dependency_overrides:
|
||||
# angel3_container:
|
||||
# path: ../container/angel_container
|
||||
# angel3_auth:
|
||||
# path: ../auth
|
||||
|
|
@ -6,8 +6,8 @@ repository: https://github.com/dukefirehawk/angel/tree/angel3/packages/hot
|
|||
environment:
|
||||
sdk: '>=2.12.0 <3.0.0'
|
||||
dependencies:
|
||||
angel3_framework: ^4.1.0
|
||||
angel3_websocket: ^4.0.0
|
||||
angel3_framework: ^4.2.0
|
||||
angel3_websocket: ^4.1.0
|
||||
belatuk_html_builder: ^3.0.0
|
||||
charcode: ^1.2.0
|
||||
glob: ^2.0.1
|
||||
|
@ -19,3 +19,7 @@ dev_dependencies:
|
|||
http: ^0.13.2
|
||||
logging: ^1.0.1
|
||||
lints: ^1.0.0
|
||||
#dependency_overrides:
|
||||
# angel3_websocket:
|
||||
# path: ../websocket
|
||||
|
|
@ -17,3 +17,6 @@ dev_dependencies:
|
|||
belatuk_pretty_logging: ^4.0.0
|
||||
test: ^1.17.5
|
||||
lints: ^1.0.0
|
||||
#dependency_overrides:
|
||||
# angel3_auth:
|
||||
# path: ../auth
|
||||
|
|
|
@ -6,11 +6,14 @@ repository: https://github.com/dukefirehawk/angel/tree/angel3/packages/sync
|
|||
environment:
|
||||
sdk: '>=2.12.0 <3.0.0'
|
||||
dependencies:
|
||||
angel3_framework: ^4.1.0
|
||||
angel3_websocket: ^4.0.0
|
||||
angel3_framework: ^4.2.0
|
||||
angel3_websocket: ^4.1.0
|
||||
belatuk_pub_sub: ^4.0.0
|
||||
stream_channel: ^2.1.0
|
||||
dev_dependencies:
|
||||
angel3_test: ^4.0.0
|
||||
test: ^1.17.8
|
||||
lints: ^1.0.0
|
||||
angel3_test: ^4.1.0
|
||||
test: ^1.17.5
|
||||
lints: ^1.0.0
|
||||
#dependency_overrides:
|
||||
# angel3_websocket:
|
||||
# path: ../websocket
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
# Change Log
|
||||
|
||||
## 4.1.1
|
||||
|
||||
* Fixed NNBD issues
|
||||
|
||||
## 4.1.0
|
||||
|
||||
* Updated linter to `package:lints`
|
||||
|
|
|
@ -6,7 +6,7 @@ import 'package:angel3_websocket/server.dart';
|
|||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
Angel? app;
|
||||
Angel app;
|
||||
late TestClient client;
|
||||
|
||||
setUp(() async {
|
||||
|
@ -46,17 +46,16 @@ void main() {
|
|||
create: (dynamic data, [params]) async => {'foo': 'bar'}));
|
||||
|
||||
var ws = AngelWebSocket(app);
|
||||
await app!.configure(ws.configureServer);
|
||||
app!.all('/ws', ws.handleRequest);
|
||||
await app.configure(ws.configureServer);
|
||||
app.all('/ws', ws.handleRequest);
|
||||
|
||||
app!.errorHandler = (e, req, res) => e.toJson();
|
||||
app.errorHandler = (e, req, res) => e.toJson();
|
||||
|
||||
client = await connectTo(app!);
|
||||
client = await connectTo(app);
|
||||
});
|
||||
|
||||
tearDown(() async {
|
||||
await client.close();
|
||||
app = null;
|
||||
});
|
||||
|
||||
group('matchers', () {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
name: angel3_test
|
||||
version: 4.1.0
|
||||
version: 4.1.1
|
||||
description: Testing utility library for the Angel3 framework. Use with package:test.
|
||||
homepage: https://angel3-framework.web.app/
|
||||
repository: https://github.com/dukefirehawk/angel/tree/master/packages/test
|
||||
|
@ -19,7 +19,7 @@ dependencies:
|
|||
dev_dependencies:
|
||||
test: ^1.17.5
|
||||
lints: ^1.0.0
|
||||
#dependency_overrides:
|
||||
# angel3_container:
|
||||
# path: ../container/angel_container
|
||||
dependency_overrides:
|
||||
angel3_websocket:
|
||||
path: ../websocket
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ import 'package:angel3_websocket/server.dart';
|
|||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
Angel? app;
|
||||
Angel app;
|
||||
late TestClient client;
|
||||
|
||||
setUp(() async {
|
||||
|
@ -48,17 +48,16 @@ void main() {
|
|||
<String, dynamic>{'foo': 'bar'}));
|
||||
|
||||
var ws = AngelWebSocket(app);
|
||||
await app!.configure(ws.configureServer);
|
||||
app!.all('/ws', ws.handleRequest);
|
||||
await app.configure(ws.configureServer);
|
||||
app.all('/ws', ws.handleRequest);
|
||||
|
||||
app!.errorHandler = (e, req, res) => e.toJson();
|
||||
app.errorHandler = (e, req, res) => e.toJson();
|
||||
|
||||
client = await connectTo(app!, useZone: false);
|
||||
client = await connectTo(app, useZone: false);
|
||||
});
|
||||
|
||||
tearDown(() async {
|
||||
await client.close();
|
||||
app = null;
|
||||
});
|
||||
|
||||
group('matchers', () {
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
# Change Log
|
||||
|
||||
## 4.1.1
|
||||
|
||||
* Fixed issue with type casting
|
||||
* Changed `app` parameter of `AngelWebSocket` to non-nullable
|
||||
|
||||
## 4.1.0
|
||||
|
||||
* Updated to use `package:belatuk_merge_map`
|
||||
|
|
|
@ -40,17 +40,17 @@ class AngelWebSocket {
|
|||
final StreamController<WebSocketContext> _onDisconnect =
|
||||
StreamController<WebSocketContext>.broadcast();
|
||||
|
||||
final Angel? app;
|
||||
final Angel app;
|
||||
|
||||
/// If this is not `true`, then all client-side service parameters will be
|
||||
/// discarded, other than `params['query']`.
|
||||
final bool allowClientParams;
|
||||
|
||||
/// An optional whitelist of allowed client origins, or [:null:].
|
||||
final List<String>? allowedOrigins;
|
||||
final List<String> allowedOrigins;
|
||||
|
||||
/// An optional whitelist of allowed client protocols, or [:null:].
|
||||
final List<String>? allowedProtocols;
|
||||
final List<String> allowedProtocols;
|
||||
|
||||
/// If `true`, then clients can authenticate their WebSockets by sending a valid JWT.
|
||||
final bool allowAuth;
|
||||
|
@ -93,8 +93,8 @@ class AngelWebSocket {
|
|||
this.synchronizationChannel,
|
||||
this.serializer,
|
||||
this.deserializer,
|
||||
this.allowedOrigins,
|
||||
this.allowedProtocols}) {
|
||||
this.allowedOrigins = const [],
|
||||
this.allowedProtocols = const []}) {
|
||||
serializer ??= json.encode;
|
||||
deserializer ??= (params) => params;
|
||||
}
|
||||
|
@ -161,7 +161,7 @@ class AngelWebSocket {
|
|||
if (e.service.configuration.containsKey('ws:filter')) {
|
||||
return e.service.configuration['ws:filter'](e, socket);
|
||||
} else if (e.params != null && e.params!.containsKey('ws:filter')) {
|
||||
return e.params!['ws:filter'](e, socket);
|
||||
return e.params?['ws:filter'](e, socket);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
|
@ -175,13 +175,15 @@ class AngelWebSocket {
|
|||
Future<void> batchEvent(WebSocketEvent event,
|
||||
{Function(WebSocketContext socket)? filter, bool notify = true}) async {
|
||||
// Default implementation will just immediately fire events
|
||||
_clients.forEach((client) async {
|
||||
for (var client in _clients) {
|
||||
dynamic result = true;
|
||||
if (filter != null) result = await filter(client);
|
||||
if (filter != null) {
|
||||
result = await filter(client);
|
||||
}
|
||||
if (result == true) {
|
||||
client.channel.sink.add((serializer ?? json.encode)(event.toJson()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (synchronizationChannel != null && notify != false) {
|
||||
synchronizationChannel!.sink.add(event);
|
||||
|
@ -200,11 +202,11 @@ class AngelWebSocket {
|
|||
return null;
|
||||
}
|
||||
|
||||
var service = app!.findService(split[0]);
|
||||
var service = app.findService(split[0]);
|
||||
|
||||
if (service == null) {
|
||||
socket.sendError(AngelHttpException.notFound(
|
||||
message: 'No service \"${split[0]}\" exists.'));
|
||||
message: 'No service "${split[0]}" exists.'));
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -222,7 +224,7 @@ class AngelWebSocket {
|
|||
|
||||
var params = mergeMap<String, dynamic>([
|
||||
(((deserializer ?? (params) => params)(action.params))
|
||||
as Map<String, dynamic>?)!,
|
||||
as Map<String, dynamic>),
|
||||
{
|
||||
'provider': Providers.websocket,
|
||||
'__requestctx': socket.request,
|
||||
|
@ -257,7 +259,7 @@ class AngelWebSocket {
|
|||
data: await service.remove(action.id, params));
|
||||
} else {
|
||||
socket.sendError(AngelHttpException.methodNotAllowed(
|
||||
message: 'Method Not Allowed: \"$actionName\"'));
|
||||
message: 'Method Not Allowed: $actionName'));
|
||||
return null;
|
||||
}
|
||||
} catch (e, st) {
|
||||
|
@ -278,7 +280,7 @@ class AngelWebSocket {
|
|||
AuthToken token;
|
||||
|
||||
token = AuthToken.validate(jwt, auth.hmac);
|
||||
var user = await auth.deserializer(token.userId as Object);
|
||||
var user = await auth.deserializer(token.userId);
|
||||
socket.request
|
||||
..container!.registerSingleton<AuthToken>(token)
|
||||
..container!.registerSingleton(user, as: user.runtimeType);
|
||||
|
@ -359,16 +361,16 @@ class AngelWebSocket {
|
|||
// Send an error
|
||||
if (e is AngelHttpException) {
|
||||
socket.sendError(e);
|
||||
app?.logger?.severe(e.message, e.error ?? e, e.stackTrace);
|
||||
app.logger?.severe(e.message, e.error ?? e, e.stackTrace);
|
||||
} else if (sendErrors) {
|
||||
var err = AngelHttpException(e,
|
||||
message: e.toString(), stackTrace: st, errors: [st.toString()]);
|
||||
socket.sendError(err);
|
||||
app?.logger?.severe(err.message, e, st);
|
||||
app.logger?.severe(err.message, e, st);
|
||||
} else {
|
||||
var err = AngelHttpException(e);
|
||||
socket.sendError(err);
|
||||
app?.logger?.severe(e.toString(), e, st);
|
||||
app.logger?.severe(e.toString(), e, st);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -413,7 +415,7 @@ class AngelWebSocket {
|
|||
/// Handles an incoming [WebSocketContext].
|
||||
Future<void> handleClient(WebSocketContext socket) async {
|
||||
var origin = socket.request.headers?.value('origin');
|
||||
if (allowedOrigins != null && !allowedOrigins!.contains(origin)) {
|
||||
if (allowedOrigins.isNotEmpty && !allowedOrigins.contains(origin)) {
|
||||
throw AngelHttpException.forbidden(
|
||||
message:
|
||||
'WebSocket connections are not allowed from the origin "$origin".');
|
||||
|
@ -479,8 +481,8 @@ class AngelWebSocket {
|
|||
throw AngelHttpException.badRequest(
|
||||
message: 'Missing `sec-websocket-key` header.');
|
||||
} else if (protocol != null &&
|
||||
allowedProtocols != null &&
|
||||
!allowedProtocols!.contains(protocol)) {
|
||||
allowedProtocols.isNotEmpty &&
|
||||
!allowedProtocols.contains(protocol)) {
|
||||
throw AngelHttpException.badRequest(
|
||||
message: 'Disallowed `sec-websocket-protocol` header "$protocol".');
|
||||
} else {
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
name: angel3_websocket
|
||||
version: 4.1.0
|
||||
version: 4.1.1
|
||||
description: This library provides WebSockets support for Angel3 framework.
|
||||
homepage: https://angel3-framework.web.app/
|
||||
repository: https://github.com/dukefirehawk/angel/tree/master/packages/websocket
|
||||
environment:
|
||||
sdk: '>=2.12.0 <3.0.0'
|
||||
dependencies:
|
||||
angel3_auth: ^4.0.0
|
||||
angel3_client: ^4.0.0
|
||||
angel3_auth: ^4.1.0
|
||||
angel3_client: ^4.1.0
|
||||
angel3_framework: ^4.2.0
|
||||
angel3_http_exception: ^3.0.0
|
||||
belatuk_merge_map: ^3.0.0
|
||||
|
@ -22,7 +22,7 @@ dev_dependencies:
|
|||
angel3_model: ^3.1.0
|
||||
test: ^1.17.5
|
||||
lints: ^1.0.0
|
||||
#dependency_overrides:
|
||||
# angel3_container:
|
||||
# path: ../container/angel_container
|
||||
dependency_overrides:
|
||||
angel3_auth:
|
||||
path: ../auth
|
||||
|
|
@ -9,7 +9,7 @@ import 'package:test/test.dart';
|
|||
import 'common.dart';
|
||||
|
||||
void main() {
|
||||
srv.Angel? app;
|
||||
srv.Angel app;
|
||||
late srv.AngelHttp http;
|
||||
ws.WebSockets? client;
|
||||
srv.AngelWebSocket websockets;
|
||||
|
@ -18,17 +18,17 @@ void main() {
|
|||
|
||||
setUp(() async {
|
||||
app = srv.Angel(reflector: const MirrorsReflector());
|
||||
http = srv.AngelHttp(app!, useZone: false);
|
||||
http = srv.AngelHttp(app, useZone: false);
|
||||
|
||||
websockets = srv.AngelWebSocket(app)
|
||||
..onData.listen((data) {
|
||||
print('Received by server: $data');
|
||||
});
|
||||
|
||||
await app!.configure(websockets.configureServer);
|
||||
app!.all('/ws', websockets.handleRequest);
|
||||
await app!.configure(GameController(websockets).configureServer);
|
||||
app!.logger = Logger('angel_auth')..onRecord.listen(print);
|
||||
await app.configure(websockets.configureServer);
|
||||
app.all('/ws', websockets.handleRequest);
|
||||
await app.configure(GameController(websockets).configureServer);
|
||||
app.logger = Logger('angel_auth')..onRecord.listen(print);
|
||||
|
||||
server = await http.startServer();
|
||||
url = 'ws://${server!.address.address}:${server!.port}/ws';
|
||||
|
@ -53,7 +53,7 @@ void main() {
|
|||
tearDown(() async {
|
||||
await client!.close();
|
||||
await http.close();
|
||||
app = null;
|
||||
//app = null;
|
||||
client = null;
|
||||
server = null;
|
||||
url = null;
|
||||
|
|
|
@ -9,7 +9,7 @@ import 'package:test/test.dart';
|
|||
import 'common.dart';
|
||||
|
||||
void main() {
|
||||
srv.Angel? app;
|
||||
srv.Angel app;
|
||||
late srv.AngelHttp http;
|
||||
ws.WebSockets? client;
|
||||
srv.AngelWebSocket websockets;
|
||||
|
@ -19,16 +19,16 @@ void main() {
|
|||
setUp(() async {
|
||||
app = srv.Angel(reflector: MirrorsReflector())
|
||||
..use('/api/todos', TodoService());
|
||||
http = srv.AngelHttp(app!, useZone: false);
|
||||
http = srv.AngelHttp(app, useZone: false);
|
||||
|
||||
websockets = srv.AngelWebSocket(app)
|
||||
..onData.listen((data) {
|
||||
print('Received by server: $data');
|
||||
});
|
||||
|
||||
await app!.configure(websockets.configureServer);
|
||||
app!.all('/ws', websockets.handleRequest);
|
||||
app!.logger = Logger('angel_auth')..onRecord.listen(print);
|
||||
await app.configure(websockets.configureServer);
|
||||
app.all('/ws', websockets.handleRequest);
|
||||
app.logger = Logger('angel_auth')..onRecord.listen(print);
|
||||
server = await http.startServer();
|
||||
url = 'ws://${server!.address.address}:${server!.port}/ws';
|
||||
|
||||
|
@ -51,7 +51,7 @@ void main() {
|
|||
await client!.close();
|
||||
await http.server!.close(force: true);
|
||||
|
||||
app = null;
|
||||
//app = null;
|
||||
client = null;
|
||||
server = null;
|
||||
url = null;
|
||||
|
|
Loading…
Reference in a new issue