1 module vibeauth.router.basic;
2 
3 import vibe.http.router;
4 import vibe.data.json;
5 import vibeauth.users;
6 import std.algorithm.searching, std.base64, std..string, std.stdio;
7 import vibeauth.router.baseAuthRouter;
8 
9 /// Basic auth credential pair
10 struct BasicAuthCredentials {
11   ///
12   string username;
13   
14   ///
15   string password;
16 }
17 
18 /// Basic auth handler. It's not safe to use it without https.
19 class BasicAuth(string realm): BaseAuthRouter {
20 
21   ///
22   this(UserCollection collection) {
23     super(collection);
24   }
25 
26   override {
27     /// Auth handler that will fail if a successfull auth was not performed.
28     /// This handler is usefull for routes that want to hide information to the
29     /// public.
30     void mandatoryAuth(HTTPServerRequest req, HTTPServerResponse res) {
31       auto pauth = "Authorization" in req.headers;
32 
33       setAccessControl(res);
34 
35       if(pauth && (*pauth).startsWith("Basic ")) {
36         auto auth = parseBasicAuth((*pauth)[6 .. $]);
37 
38         if(auth.username in collection && collection[auth.username].isValidPassword(auth.password)) {
39           req.username = auth.username;
40           return;
41         } else {
42           respondUnauthorized(res);
43         }
44       } else {
45         respondUnauthorized(res);
46       }
47     }
48 
49     /// Auth handler that fails only if the auth fields are present and are not valid.
50     /// This handler is usefull when a route should return different data when the user is 
51     /// logged in
52     void permisiveAuth(HTTPServerRequest req, HTTPServerResponse res) {
53       auto pauth = "Authorization" in req.headers;
54 
55       setAccessControl(res);
56 
57       if(pauth && (*pauth).startsWith("Basic ")) {
58         auto auth = parseBasicAuth((*pauth)[6 .. $]);
59 
60         if(auth.username in collection && collection[auth.username].isValidPassword(auth.password)) {
61           req.username = auth.username;
62           return;
63         } else {
64           respondUnauthorized(res);
65         }
66       }
67     }
68   }
69 
70   private {
71     /// Parse user input
72     BasicAuthCredentials parseBasicAuth(string data) {
73       string decodedData = cast(string)Base64.decode(data);
74       auto idx = decodedData.indexOf(":");
75       enforceBadRequest(idx >= 0, "Invalid auth string format!");
76 
77       return BasicAuthCredentials(decodedData[0 .. idx], decodedData[idx+1 .. $]);
78     }
79 
80     /// Respond with auth error
81     void respondUnauthorized(HTTPServerResponse res, string message = "Authorization required") {
82       res.statusCode = HTTPStatus.unauthorized;
83       res.contentType = "text/plain";
84       res.headers["WWW-Authenticate"] = "Basic realm=\""~realm~"\"";
85       res.bodyWriter.write(message);
86     }
87   }
88 }