diff --git a/.gitignore b/.gitignore index c947f260..d1fc3f51 100644 --- a/.gitignore +++ b/.gitignore @@ -73,3 +73,4 @@ crashlytics.properties crashlytics-build.properties fabric.properties +logs/**/*.txt \ No newline at end of file diff --git a/lib/src/routes/controllers/auth.dart b/lib/src/routes/controllers/auth.dart index 53ff0179..194a3744 100644 --- a/lib/src/routes/controllers/auth.dart +++ b/lib/src/routes/controllers/auth.dart @@ -4,16 +4,24 @@ part of angel.routes.controllers; class AuthController extends Controller { @override call(Angel app) async { + await super.call(app); + app.registerMiddleware("auth", (req, res) async { - if (req.session['userId'] == null) - throw new AngelHttpException.Forbidden(); + if (!loggedIn(req)) throw new AngelHttpException.Forbidden(); return true; }); } + bool loggedIn(RequestContext req) => req.session["userId"] != null; + @Expose("/login", method: "POST") login(RequestContext req) async { // Include log-in logic here... } + + @Expose("/register", method: "POST") + register(RequestContext req, UserService Users) async { + // And your registration logic... + } } diff --git a/lib/src/routes/controllers/controllers.dart b/lib/src/routes/controllers/controllers.dart index 15f395f7..3964d592 100644 --- a/lib/src/routes/controllers/controllers.dart +++ b/lib/src/routes/controllers/controllers.dart @@ -2,6 +2,7 @@ library angel.routes.controllers; import 'package:angel_auth/angel_auth.dart'; import 'package:angel_framework/angel_framework.dart'; +import '../../services/user/user.dart'; part 'auth.dart'; configureServer(Angel app) async { diff --git a/lib/src/routes/routes.dart b/lib/src/routes/routes.dart index 954eee24..347d470b 100644 --- a/lib/src/routes/routes.dart +++ b/lib/src/routes/routes.dart @@ -15,18 +15,18 @@ configureRoutes(Angel app) async { configureAfter(Angel app) async { // 404 handler - app.after.add((RequestContext req, ResponseContext res) async { - res.willCloseItself = true; - res.status(404); - res.header('Content-Type', 'text/html'); - res.underlyingResponse - .write(await app.viewGenerator('404', {'path': req.path})); - await res.underlyingResponse.close(); - }); + app.after.add((req, res) async => res + ..status(404) + ..render("404", {"path": req.path})); + + // Default error handler + app.onError( + (e, req, res) async => res.render("error", {"message": e.message})); } configureServer(Angel app) async { await configureBefore(app); await configureRoutes(app); + await app.configure(Controllers.configureServer); await configureAfter(app); } diff --git a/lib/src/services/services.dart b/lib/src/services/services.dart index 7e107dd4..966bf387 100644 --- a/lib/src/services/services.dart +++ b/lib/src/services/services.dart @@ -4,11 +4,11 @@ library angel.services; import 'package:angel_framework/angel_framework.dart'; import 'package:mongo_dart/mongo_dart.dart'; -import 'users/users.dart' as Users; +import 'user/user.dart' as User; configureServer(Angel app) async { Db db = new Db(app.properties["mongo_db"]); await db.open(); - await app.configure(Users.configureServer(db)); + await app.configure(User.configureServer(db)); } diff --git a/lib/src/services/user/user.dart b/lib/src/services/user/user.dart new file mode 100644 index 00000000..0c733788 --- /dev/null +++ b/lib/src/services/user/user.dart @@ -0,0 +1,59 @@ +import 'package:angel_framework/angel_framework.dart'; +import 'package:angel_mongo/angel_mongo.dart'; +import 'package:crypto/crypto.dart' show sha256; +import 'package:mongo_dart/mongo_dart.dart'; +import 'package:validate/validate.dart'; +import '../../models/user.dart'; +export '../../models/user.dart'; + +configureServer(Db db) { + return (Angel app) async { + app.use("/api/users", new UserService(db.collection("users"))); + + HookedService service = app.service("api/users"); + app.container.singleton(service.inner); + }; +} + +/// 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 Service { + MongoTypedService _inner; + + UserService(DbCollection collection):super() { + _inner = new MongoTypedService(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 _inner.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(); + } + + try { + Validate.isKeyInMap("username", data); + Validate.isEmail(data["email"]); + data["password"] = sha256.convert(data["password"].codeUnits).toString(); + } catch(e) { + throw new AngelHttpException.BadRequest(message: "User must have a username, e-mail address and password."); + } + + return _inner.create(data, params); + } + + @override + read(id, [Map params]) => _inner.read(id, params); +} diff --git a/lib/src/services/users/users.dart b/lib/src/services/users/users.dart deleted file mode 100644 index bd7532c9..00000000 --- a/lib/src/services/users/users.dart +++ /dev/null @@ -1,32 +0,0 @@ -import 'package:angel_framework/angel_framework.dart'; -import 'package:angel_mongo/angel_mongo.dart'; -import 'package:crypto/crypto.dart' show sha256; -import 'package:mongo_dart/mongo_dart.dart'; -import 'package:validate/validate.dart'; -import '../../models/user.dart'; - - -hashPassword(HookedServiceEvent event) { - if (event.data.password != null) { - event.data.password = - sha256.convert(event.data.password.codeUnits).toString(); - } -} - -configureServer(Db db) { - return (Angel app) async { - app.use("/api/users", new MongoTypedService(db.collection("users"))); - - HookedService service = app.service("api/users"); - - // Place your hooks here! - - service.beforeCreated.listen((HookedServiceEvent e) { - Validate.isKeyInMap("username", e.data); - Validate.isEmail(e.data["email"]); - Validate.isPassword(e.data["password"]); - }); - - service.beforeCreated.listen(hashPassword); - }; -} diff --git a/logs/2016-09-20.txt b/logs/2016-09-20.txt deleted file mode 100644 index 070aeb08..00000000 --- a/logs/2016-09-20.txt +++ /dev/null @@ -1,3 +0,0 @@ -INFO: 2016-09-20 00:52:14.862305: Server listening at http://127.0.0.1:3000 -CONFIG: 2016-09-20 00:52:14.864663: Starting server with given configuration: {mongo_db: mongodb://localhost:27017/angel, db: {web: {database: angel, password: password, username: username}, migrate: {database: angel, password: password, username: root}}, host: 127.0.0.1, port: 3000} -INFO: 2016-09-20 00:52:20.711300: 200 GET / (69 ms) diff --git a/views/error.mustache b/views/error.mustache new file mode 100644 index 00000000..1b0226b4 --- /dev/null +++ b/views/error.mustache @@ -0,0 +1,49 @@ + + + + {{message}} + + + + + + +
+
+
{{message}}
+
+
+ + \ No newline at end of file