platform/test/current_user_test.dart
2019-04-20 12:37:50 -04:00

130 lines
3.9 KiB
Dart

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<String, T, Service<String, T>> serve<T>(String path,
T Function(Map) encoder, Map<String, dynamic> Function(T) decoder) {
var inner = MapService();
var mapped = inner.map(encoder, decoder);
return app.use<String, T, Service<String, T>>(path, mapped);
}
var userService =
serve<User>('/api/users', User.fromMap, (u) => u.toJson());
var houseService =
serve<House>('/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<Future<User>>((container) async {
var req = container.make<RequestContext>();
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<String, User>(
as: 'owner_id',
getId: (user) => user.id,
));
// A house is associated with the current user.
houseService.beforeCreated
.listen(hooks.associateCurrentUser<String, House, User>(
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<String, dynamic> 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) => House(
map['id'] as String, map['owner_id'] as String, map['address'] as String);
House withOwner(String newOwnerId) {
return House(id, newOwnerId, address);
}
Map<String, dynamic> toJson() {
return {'id': id, 'owner_id': ownerId, 'address': address};
}
}