1 /** 2 * Copyright © DiamondMVC 2018 3 * License: MIT (https://github.com/DiamondMVC/Diamond/blob/master/LICENSE) 4 * Author: Jacob Jensen (bausshf) 5 */ 6 module diamond.authentication.auth; 7 8 import diamond.core.apptype; 9 10 static if (isWeb) 11 { 12 import core.time : minutes; 13 import std.datetime : Clock; 14 15 import diamond.http; 16 import diamond.errors.checks; 17 import diamond.authentication.roles; 18 19 /// The token validator. 20 private static __gshared TokenValidator tokenValidator; 21 22 /// The token setter. 23 private static __gshared TokenSetter tokenSetter; 24 25 /// The token invalidator. 26 private static __gshared TokenInvalidator tokenInvalidator; 27 28 /// Static constructor for the module. 29 shared static this() 30 { 31 tokenValidator = new TokenValidator; 32 tokenSetter = new TokenSetter; 33 tokenInvalidator = new TokenInvalidator; 34 } 35 36 /// The cookie key for auth tokens. 37 private static const __gshared authCookieKey = "__D_AUTH_TOKEN"; 38 39 /// Wrapper for the token validator. 40 private class TokenValidator 41 { 42 /// Function pointer. 43 Role function(string,HttpClient) f; 44 45 /// Delegate. 46 Role delegate(string,HttpClient) d; 47 48 /** 49 * Validates the token. 50 * Params: 51 * token = The token to validate. 52 * client = The client. 53 * Returns: 54 * The role to associate with the token. 55 */ 56 Role validate(string token, HttpClient client) 57 { 58 if (f) return f(token, client); 59 else if (d) return d(token, client); 60 61 return null; 62 } 63 } 64 65 /// Wrapper for the token setter. 66 private class TokenSetter 67 { 68 /// Function pointer. 69 string function(HttpClient) f; 70 71 /// Delegate. 72 string delegate(HttpClient) d; 73 74 /** 75 * Sets the token and gets the result. 76 * Params: 77 * client = The client. 78 * Returns: 79 * Returns the token result. This should be the generated token. 80 */ 81 string getAndSetToken(HttpClient client) 82 { 83 if (f) return f(client); 84 else if (d) return d(client); 85 86 return null; 87 } 88 } 89 90 /// Wrapper for the token invalidator. 91 private class TokenInvalidator 92 { 93 /// Function pointer. 94 void function(string,HttpClient) f; 95 96 /// Delegate. 97 void delegate(string,HttpClient) d; 98 99 /** 100 * Invalidates the token. 101 * Params: 102 * token = The token to invalidate. 103 * client = The client. 104 */ 105 void invalidate(string token, HttpClient client) 106 { 107 if (f) f(token, client); 108 else if (d) d(token, client); 109 } 110 } 111 112 /** 113 * Sets the token validator. 114 * Params: 115 * validator = The validator. 116 */ 117 void setTokenValidator(Role function(string,HttpClient) validator) 118 { 119 tokenValidator.f = validator; 120 tokenValidator.d = null; 121 } 122 123 /// ditto. 124 void setTokenValidator(Role delegate(string,HttpClient) validator) 125 { 126 tokenValidator.f = null; 127 tokenValidator.d = validator; 128 } 129 130 /** 131 * Sets the token setter. 132 * Params: 133 * setter = The setter. 134 */ 135 void setTokenSetter(string function(HttpClient) setter) 136 { 137 tokenSetter.f = setter; 138 tokenSetter.d = null; 139 } 140 141 /// Ditto. 142 void setTokenSetter(string delegate(HttpClient) setter) 143 { 144 tokenSetter.f = null; 145 tokenSetter.d = setter; 146 } 147 148 /** 149 * Sets the token invalidator. 150 * Params: 151 * invalidator = The invalidator. 152 */ 153 void setTokenInvalidator(void function(string,HttpClient) invalidator) 154 { 155 tokenInvalidator.f = invalidator; 156 tokenInvalidator.d = null; 157 } 158 159 /// Ditto. 160 void setTokenInvalidator(void delegate(string,HttpClient) invalidator) 161 { 162 tokenInvalidator.f = null; 163 tokenInvalidator.d = invalidator; 164 } 165 166 /** 167 * Validates the authentication. 168 * This also sets the role etc. 169 * Params: 170 * client = The client. 171 */ 172 void validateAuthentication(HttpClient client) 173 { 174 if (setRoleFromSession(client, true)) 175 { 176 return; 177 } 178 179 enforce(tokenValidator.f !is null || tokenValidator.d !is null, "No token validator found."); 180 181 auto token = client.cookies.get(authCookieKey); 182 Role role; 183 184 if (token) 185 { 186 role = tokenValidator.validate(token, client); 187 } 188 189 if (!role) 190 { 191 role = getRole(""); 192 } 193 194 setRole(client, role); 195 } 196 197 /** 198 * Logs the user in. 199 * Params: 200 * client = The client. 201 * loginTime = The time the user can be logged in. (In minutes) 202 * role = The role to login as. (If the role is null then the session won't have a role, causing every request to be authenticated.) 203 */ 204 void login(HttpClient client, long loginTime, Role role) 205 { 206 enforce(tokenSetter.f !is null || tokenSetter.d !is null, "No token setter found."); 207 208 if (role !is null) 209 { 210 setSessionRole(client, role); 211 } 212 213 client.session.updateEndTime(Clock.currTime() + loginTime.minutes); 214 215 auto token = enforceInput(tokenSetter.getAndSetToken(client), "Could not set token."); 216 217 client.cookies.create(authCookieKey, token, loginTime * 60); 218 219 validateAuthentication(client); 220 } 221 222 /** 223 * Logs the user out. 224 * Params: 225 * client = The client. 226 */ 227 void logout(HttpClient client) 228 { 229 enforce(tokenInvalidator.f !is null || tokenInvalidator.d !is null, "No token invalidator found."); 230 231 client.session.clearValues(); 232 client.cookies.remove(authCookieKey); 233 setRoleFromSession(client, false); 234 235 auto token = client.cookies.get(authCookieKey); 236 237 if (token) 238 { 239 tokenInvalidator.invalidate(token, client); 240 } 241 } 242 243 /** 244 * Gets the auth cookie from a client. 245 * Params: 246 * client = The client to get the auth cookie from. 247 * Returns: 248 * Returns the auth cookie. 249 */ 250 string getAuthCookie(HttpClient client) 251 { 252 return client.cookies.get(authCookieKey); 253 } 254 255 /** 256 * Checks whether the client has the auth cookie or not. 257 * Params: 258 * client = The client. 259 * Returns: 260 * True if the client has the auth cookie, false otherwise. 261 */ 262 bool hasAuthCookie(HttpClient client) 263 { 264 return client.cookies.has(authCookieKey); 265 } 266 }