Hooks
This commit is contained in:
parent
84bcb05fdd
commit
cbddcc72a4
3 changed files with 28 additions and 42 deletions
|
@ -4,7 +4,7 @@ import 'package:angel_framework/common.dart';
|
||||||
|
|
||||||
class User extends Model {
|
class User extends Model {
|
||||||
String email, username, password, salt;
|
String email, username, password, salt;
|
||||||
final List<String> roles = [];
|
List<String> roles;
|
||||||
|
|
||||||
User(
|
User(
|
||||||
{String id,
|
{String id,
|
||||||
|
@ -12,8 +12,7 @@ class User extends Model {
|
||||||
this.username,
|
this.username,
|
||||||
this.password,
|
this.password,
|
||||||
this.salt,
|
this.salt,
|
||||||
Iterable<String> roles: const []}) {
|
this.roles: const []}) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.roles.addAll(roles ?? []);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,11 +7,16 @@ import '../../services/user.dart';
|
||||||
class AuthController extends Controller {
|
class AuthController extends Controller {
|
||||||
AngelAuth auth;
|
AngelAuth auth;
|
||||||
|
|
||||||
deserializer(String id) async => app.service('api/users').read(id);
|
/// Clients will see the result of `deserializer`, so let's pretend to be a client.
|
||||||
|
///
|
||||||
|
/// Our User service is already wired to remove sensitive data from serialized JSON.
|
||||||
|
deserializer(String id) async =>
|
||||||
|
app.service('api/users').read(id, {'provider': Providers.VIA_REST});
|
||||||
|
|
||||||
serializer(User user) async => user.id;
|
serializer(User user) async => user.id;
|
||||||
|
|
||||||
/// Attempt to log a user in
|
/// Attempt to log a user in
|
||||||
LocalAuthVerifier localVerifier(UserService userService) {
|
LocalAuthVerifier localVerifier(Service userService) {
|
||||||
return (String username, String password) async {
|
return (String username, String password) async {
|
||||||
List<User> users = await userService.index({
|
List<User> users = await userService.index({
|
||||||
'query': {'username': username}
|
'query': {'username': username}
|
||||||
|
@ -32,8 +37,8 @@ class AuthController extends Controller {
|
||||||
auth = new AngelAuth(jwtKey: app.jwt_secret)
|
auth = new AngelAuth(jwtKey: app.jwt_secret)
|
||||||
..serializer = serializer
|
..serializer = serializer
|
||||||
..deserializer = deserializer
|
..deserializer = deserializer
|
||||||
..strategies.add(new LocalAuthStrategy(
|
..strategies
|
||||||
localVerifier(app.container.make(UserService))));
|
.add(new LocalAuthStrategy(localVerifier(app.service('api/users'))));
|
||||||
|
|
||||||
await super.call(app);
|
await super.call(app);
|
||||||
await app.configure(auth);
|
await app.configure(auth);
|
||||||
|
@ -41,9 +46,4 @@ class AuthController extends Controller {
|
||||||
|
|
||||||
@Expose('/local', method: 'POST')
|
@Expose('/local', method: 'POST')
|
||||||
login() => auth.authenticate('local');
|
login() => auth.authenticate('local');
|
||||||
|
|
||||||
@Expose('/register', method: 'POST')
|
|
||||||
register(RequestContext req, UserService userService) async {
|
|
||||||
// And your registration logic...
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import 'package:angel_common/angel_common.dart';
|
import 'package:angel_common/angel_common.dart';
|
||||||
|
import 'package:angel_framework/hooks.dart' as hooks;
|
||||||
import 'package:crypto/crypto.dart' show sha256;
|
import 'package:crypto/crypto.dart' show sha256;
|
||||||
import 'package:mongo_dart/mongo_dart.dart';
|
import 'package:mongo_dart/mongo_dart.dart';
|
||||||
import 'package:random_string/random_string.dart' as rs;
|
import 'package:random_string/random_string.dart' as rs;
|
||||||
|
@ -8,11 +9,22 @@ export '../models/user.dart';
|
||||||
|
|
||||||
configureServer(Db db) {
|
configureServer(Db db) {
|
||||||
return (Angel app) async {
|
return (Angel app) async {
|
||||||
app.use('/api/users', new UserService(db.collection('users')));
|
app.use('/api/users',
|
||||||
|
new TypedService<User>(new MongoService(db.collection('users'))));
|
||||||
|
|
||||||
HookedService service = app.service('api/users');
|
HookedService service = app.service('api/users');
|
||||||
app.container.singleton(service.inner);
|
|
||||||
|
|
||||||
|
// Prevent clients from doing anything to the `users` service,
|
||||||
|
// apart from reading a single user's data.
|
||||||
|
service.before([
|
||||||
|
HookedServiceEvent.INDEXED,
|
||||||
|
HookedServiceEvent.CREATED,
|
||||||
|
HookedServiceEvent.MODIFIED,
|
||||||
|
HookedServiceEvent.UPDATED,
|
||||||
|
HookedServiceEvent.REMOVED
|
||||||
|
], hooks.disable());
|
||||||
|
|
||||||
|
// Validate new users, and also hash their passwords.
|
||||||
service.beforeCreated
|
service.beforeCreated
|
||||||
..listen(validateEvent(CREATE_USER))
|
..listen(validateEvent(CREATE_USER))
|
||||||
..listen((e) {
|
..listen((e) {
|
||||||
|
@ -22,37 +34,12 @@ configureServer(Db db) {
|
||||||
hashPassword(e.data['password'], salt, app.jwt_secret)
|
hashPassword(e.data['password'], salt, app.jwt_secret)
|
||||||
..['salt'] = salt;
|
..['salt'] = salt;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Remove sensitive data from serialized JSON.
|
||||||
|
service.afterAll(hooks.remove(['password', 'salt']));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// SHA-256 hash any string, particularly a password.
|
/// SHA-256 hash any string, particularly a password.
|
||||||
String hashPassword(String password, String salt, String pepper) =>
|
String hashPassword(String password, String salt, String pepper) =>
|
||||||
sha256.convert(('$salt:$password:$pepper').codeUnits).toString();
|
sha256.convert(('$salt:$password:$pepper').codeUnits).toString();
|
||||||
|
|
||||||
/// Manages users.
|
|
||||||
///
|
|
||||||
/// Here, we extended the base service class. This allows to only expose
|
|
||||||
/// specific methods, and also allows more freedom over things such as validation.
|
|
||||||
class UserService extends TypedService<User> {
|
|
||||||
UserService(DbCollection collection) : super(new MongoService(collection));
|
|
||||||
|
|
||||||
@override
|
|
||||||
index([Map params]) {
|
|
||||||
if (params != null && params.containsKey('provider')) {
|
|
||||||
// Nobody needs to see the entire user list except for the server.
|
|
||||||
throw new AngelHttpException.forbidden();
|
|
||||||
}
|
|
||||||
|
|
||||||
return super.index(params);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
create(data, [Map params]) {
|
|
||||||
if (params != null && params.containsKey('provider')) {
|
|
||||||
// Deny creating users to the public - this should be done by the server only.
|
|
||||||
throw new AngelHttpException.forbidden();
|
|
||||||
}
|
|
||||||
|
|
||||||
return super.create(data, params);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in a new issue