Skip to content

Commit

Permalink
Merge pull request #6 from bausshf/master
Browse files Browse the repository at this point in the history
Added authentication to the ACL
  • Loading branch information
bausshf authored Oct 23, 2017
2 parents 9cfc3da + 38b1ebd commit d6c8840
Show file tree
Hide file tree
Showing 6 changed files with 363 additions and 10 deletions.
253 changes: 253 additions & 0 deletions src/authentication/auth.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@
/**
* Copyright © DiamondMVC 2016-2017
* License: MIT (https://github.com/DiamondMVC/Diamond/blob/master/LICENSE)
* Author: Jacob Jensen (bausshf)
*/
module diamond.authentication.auth;

import diamond.core.apptype;

static if (isWeb)
{
import core.time : minutes;
import std.datetime : Clock;

import vibe.d : HTTPServerRequest, HTTPServerResponse;

import diamond.http;
import diamond.errors.checks;
import diamond.authentication.roles;

/// The token validator.
private static __gshared TokenValidator tokenValidator;

/// The token setter.
private static __gshared TokenSetter tokenSetter;

/// The token invalidator.
private static __gshared TokenInvalidator tokenInvalidator;

/// Static constructor for the module.
shared static this()
{
tokenValidator = new TokenValidator;
tokenSetter = new TokenSetter;
tokenInvalidator = new TokenInvalidator;
}

/// The cookie key for auth tokens.
private static const __gshared authCookieKey = "__D_AUTH_TOKEN";

/// Wrapper for the token validator.
private class TokenValidator
{
/// Function pointer.
Role function(string,HTTPServerRequest,HTTPServerResponse) f;

/// Delegate.
Role delegate(string,HTTPServerRequest,HTTPServerResponse) d;

/**
* Validates the token.
* Params:
* token = The token to validate.
* request = The request.
* response = The response.
* Returns:
* The role to associate with the token.
*/
Role validate(string token, HTTPServerRequest request, HTTPServerResponse response)
{
if (f) return f(token, request, response);
else if (d) return d(token, request, response);

return null;
}
}

/// Wrapper for the token setter.
private class TokenSetter
{
/// Function pointer.
string function(HTTPServerRequest,HTTPServerResponse) f;

/// Delegate.
string delegate(HTTPServerRequest,HTTPServerResponse) d;

/**
* Sets the token and gets the result.
* Params:
* request = The request.
* response = The response.
* Returns:
* Returns the token result. This should be the generated token.
*/
string getAndSetToken(HTTPServerRequest request, HTTPServerResponse response)
{
if (f) return f(request, response);
else if (d) return d(request, response);

return null;
}
}

/// Wrapper for the token invalidator.
private class TokenInvalidator
{
/// Function pointer.
void function(string,HTTPServerRequest,HTTPServerResponse) f;

/// Delegate.
void delegate(string,HTTPServerRequest,HTTPServerResponse) d;

/**
* Invalidates the token.
* Params:
* token = The token to invalidate.
* request = The request.
* response = The response.
*/
void invalidate(string token, HTTPServerRequest request, HTTPServerResponse response)
{
if (f) f(token, request, response);
else if (d) d(token, request, response);
}
}

/**
* Sets the token validator.
* Params:
* validator = The validator.
*/
void setTokenValidator(Role function(string,HTTPServerRequest,HTTPServerResponse) validator)
{
tokenValidator.f = validator;
tokenValidator.d = null;
}

/// ditto.
void setTokenValidator(Role delegate(string,HTTPServerRequest,HTTPServerResponse) validator)
{
tokenValidator.f = null;
tokenValidator.d = validator;
}

/**
* Sets the token setter.
* Params:
* setter = The setter.
*/
void setTokenSetter(string function(HTTPServerRequest,HTTPServerResponse) setter)
{
tokenSetter.f = setter;
tokenSetter.d = null;
}

/// Ditto.
void setTokenSetter(string delegate(HTTPServerRequest,HTTPServerResponse) setter)
{
tokenSetter.f = null;
tokenSetter.d = setter;
}

/**
* Sets the token invalidator.
* Params:
* invalidator = The invalidator.
*/
void setTokenInvalidator(void function(string,HTTPServerRequest,HTTPServerResponse) invalidator)
{
tokenInvalidator.f = invalidator;
tokenInvalidator.d = null;
}

/// Ditto.
void setTokenInvalidator(void delegate(string,HTTPServerRequest,HTTPServerResponse) invalidator)
{
tokenInvalidator.f = null;
tokenInvalidator.d = invalidator;
}

/**
* Validates the authentication.
* This also sets the role etc.
* Params:
* request = The request.
* response = The response.
*/
void validateAuthentication(HTTPServerRequest request, HTTPServerResponse response)
{
enforce(request, "No request found.");
enforce(response, "No response found.");

if (setRoleFromSession(request, response, true))
{
return;
}

enforce(tokenValidator.f !is null || tokenValidator.d !is null, "No token validator found.");

auto token = request.getCookie(authCookieKey);
Role role;

if (token)
{
role = tokenValidator.validate(token, request, response);
}

if (!role)
{
role = getRole("");
}

setRole(request, role);
}

/**
* Logs the user in.
* Params:
* request = The request.
* responsse = The response.
* loginTime = The time the user can be logged in. (In minutes)
* role = The role to login as.
*/
void login(HTTPServerRequest request, HTTPServerResponse response, long loginTime, Role role)
{
enforce(request, "No request found.");
enforce(response, "No response found.");
enforce(tokenSetter.f !is null || tokenSetter.d !is null, "No token setter found.");

setSessionRole(request, response, role);
updateSessionEndTime(request, response, Clock.currTime() + loginTime.minutes);

auto token = enforceInput(tokenSetter.getAndSetToken(request, response), "Could not set token.");

response.createCookie(authCookieKey, token, loginTime * 60);

validateAuthentication(request, response);
}

/**
* Logs the user out.
* Params:
* request = The request.
* response = The response.
*/
void logout(HTTPServerRequest request, HTTPServerResponse response)
{
enforce(request, "No request found.");
enforce(response, "No response found.");
enforce(tokenInvalidator.f !is null || tokenInvalidator.d !is null, "No token invalidator found.");

clearSessionValues(request, response);
response.removeCookie(authCookieKey);
setRoleFromSession(request, response, false);

auto token = request.getCookie(authCookieKey);

if (token)
{
tokenInvalidator.invalidate(token, request, response);
}
}
}
1 change: 1 addition & 0 deletions src/authentication/package.d
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ public
{
import diamond.authentication.roles;
import diamond.authentication.permissions;
import diamond.authentication.auth;
}
69 changes: 67 additions & 2 deletions src/authentication/roles.d
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@ import diamond.core.apptype;

static if (isWeb)
{
import vibe.d : HTTPServerRequest;
import vibe.d : HTTPServerRequest, HTTPServerResponse;

import diamond.errors.checks;
import diamond.authentication.permissions;
import diamond.http;

/// The storage key for the authentication roles.
private static const __gshared roleStorageKey = "__D_AUTH_ROLE";
Expand All @@ -24,7 +25,7 @@ static if (isWeb)
private static __gshared Role defaultRole;

/// Gets a boolean determining whether there are roles or not.
@property bool hasRoles() { return _roles.length > 0; }
@property bool hasRoles() { return _roles.length > 0 && defaultRole !is null; }

/// Wrapper around a role.
final class Role
Expand Down Expand Up @@ -207,6 +208,70 @@ static if (isWeb)
return role.hasValue ? role.get!Role : defaultRole;
}

/**
* Sets the role.
* Params:
* request = The request.
* role = The role.
*/
package(diamond.authentication) void setRole(HTTPServerRequest request, Role role)
{
enforce(request, "No request specified.");
enforce(role, "No role specified.");

request.context[roleStorageKey] = role;
}

/**
* Sets the role from the session.
* Params:
* request = The request.
* response = The response.
* defaultIsInvalid = Boolean determining whether the default role is an invalid role.
* Returns:
* Returns true if the role was set from the session.
*/
package(diamond.authentication) bool setRoleFromSession
(
HTTPServerRequest request, HTTPServerResponse response,
bool defaultIsInvalid
)
{
enforce(request, "No request specified.");

auto sessionRole = getSessionValue(request, response, roleStorageKey, null);

if (sessionRole !is null)
{
auto role = getRole(sessionRole);

if (defaultIsInvalid && role == defaultRole)
{
return false;
}

setRole(request, role);
return true;
}

return false;
}

/**
* Sets the session role.
* Params:
* request = The request.
* response = The response.
* role = The role.
*/
package(diamond.authentication) void setSessionRole
(
HTTPServerRequest request, HTTPServerResponse response, Role role
)
{
setSessionValue(request, response, roleStorageKey, role.name);
}

/**
* Sets the default role.
* Params:
Expand Down
1 change: 1 addition & 0 deletions src/controllers/controller.d
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ static if (isWeb)
import std.traits : hasUDA, getUDAs;

import vibe.d;
public import vibe.d : HTTPMethod;

import diamond.controllers.action;
import diamond.controllers.status;
Expand Down
3 changes: 1 addition & 2 deletions src/diamondapp.d
Original file line number Diff line number Diff line change
Expand Up @@ -172,8 +172,7 @@ static if (isWeb)

if (hasRoles)
{
//validateAuthentication(); // checks the authentication and validates it
// also sets the role in the session ...
validateAuthentication(request, response);

auto role = getRole(request);

Expand Down
Loading

0 comments on commit d6c8840

Please sign in to comment.