import 'dart:async'; import 'package:angel_framework/angel_framework.dart'; import 'package:angel_security/angel_security.dart'; import 'package:angel_security/hooks.dart' as hooks; import 'package:angel_test/angel_test.dart'; import 'package:crypto/crypto.dart'; import 'package:logging/logging.dart'; import 'package:pedantic/pedantic.dart'; import 'package:test/test.dart'; import 'pretty_logging.dart'; void main() { Angel app; TestClient client; setUp(() async { var logger = Logger.detached('hooks_test')..onRecord.listen(prettyLog); app = Angel(logger: logger); client = await connectTo(app); HookedService> serve(String path, T Function(Map) encoder, Map Function(T) decoder) { var inner = MapService(); var mapped = inner.map(encoder, decoder); return app.use>(path, mapped); } var userService = serve('/api/users', User.fromMap, (u) => u.toJson()); var houseService = serve('/api/houses', House.fromMap, (h) => h.toJson()); // Seed things up. var pSherman = await userService.create(User('0', 'P Sherman')); await houseService.create(House('0', pSherman.id, '42 Wallaby Way')); await houseService.create(House('1', pSherman.id, 'Finding Nemo')); await houseService .create(House('1', '4', 'Should Not Appear for P. Sherman')); // Inject a user depending on the authorization header. app.container.registerFactory>((container) async { var req = container.make(); var authValue = req.headers.value('authorization')?.replaceAll('Bearer', '')?.trim(); if (authValue == null) throw AngelHttpException.badRequest( message: 'Missing "authorization".'); var user = await userService.read(authValue).catchError((_) => null); if (user == null) throw AngelHttpException.notAuthenticated( message: 'Invalid "authorization" ($authValue).'); return user; }); // ACCESS CONTROL: // A user can only see their own houses. houseService.beforeIndexed.listen(hooks.queryWithCurrentUser( as: 'owner_id', getId: (user) => user.id, )); // A house is associated with the current user. houseService.beforeCreated .listen(hooks.associateCurrentUser( getId: (user) => user.id, assignUserId: (id, house) => house.withOwner(id), )); }); tearDown(() async { app.logger.clearListeners(); await app.close(); unawaited(client.close()); }); test('query with current user', () async { client.authToken = '0'; var houseService = client.service('/api/houses'); expect(await houseService.index(), [ {'id': '0', 'owner_id': '0', 'address': '42 Wallaby Way'}, {'id': '1', 'owner_id': '0', 'address': 'Finding Nemo'} ]); }); test('associate current user', () async { client.authToken = '0'; var houseService = client.service('/api/houses'); expect( await houseService.create({'address': 'Hello'}), allOf( containsPair('address', 'Hello'), containsPair('owner_id', '0'), )); }); } class User { final String id; final String name; User(this.id, this.name); static User fromMap(Map map) => User(map['id'] as String, map['name'] as String); Map toJson() { return {'id': id, 'name': name}; } } class House { final String id; final String ownerId; final String address; House(this.id, this.ownerId, this.address); static House fromMap(Map map) { print('In $map'); return House(map['id'] as String, map['owner_id'] as String, map['address'] as String); } House withOwner(String newOwnerId) { return House(id, newOwnerId, address); } Map toJson() { print('Out ${{'id': id, 'owner_id': ownerId, 'address': address}}'); return {'id': id, 'owner_id': ownerId, 'address': address}; } }