Skip to content
Permalink
Browse files

Implement Auth, AuthRedirect and custom attributes for routes

  • Loading branch information...
Kripth committed Jan 4, 2019
1 parent b7e2600 commit 5bcc52565aff6d698c16c7837a904a6338b00ef0
Showing with 141 additions and 41 deletions.
  1. +1 −1 dub.sdl
  2. +8 −6 src/scorpion/controller.d
  3. +1 −1 src/scorpion/package.d
  4. +28 −11 src/scorpion/register.d
  5. +44 −20 src/scorpion/session.d
  6. +5 −1 src/scorpion/starter.d
  7. +1 −1 src/scorpion/validation.d
  8. +53 −0 src/scorpion/welcome.d
@@ -5,5 +5,5 @@ license "MIT"
dependency "asdf" version="~>0.4.3"
dependency "diet-ng" version="~>1.5.0"
dependency "my-ip" version="~>0.2.0"
dependency "lighttp" version="~>0.4.0"
dependency "lighttp" version="~>0.5.0"
dependency "shark" version="~>0.1.1"
@@ -24,33 +24,35 @@ struct Controller {
struct Route {

string method;
bool hasBody;
string[] path;

this(string method, string[] path...) {
this(string method, bool hasBody, string[] path...) {
this.method = method;
this.hasBody = hasBody;
this.path = path;
}

}

Route Get(string[] path...) {
return Route("GET", path);
return Route("GET", false, path);
}

Route Post(string[] path...) {
return Route("POST", path);
return Route("POST", true, path);
}

Route Put(string[] path...) {
return Route("PUT", path);
return Route("PUT", true, path);
}

Route Patch(string[] path...) {
return Route("PATCH", path);
return Route("PATCH", true, path);
}

Route Delete(string[] path...) {
return Route("DELETE", path);
return Route("DELETE", true, path);
}

enum Path;
@@ -8,7 +8,7 @@ public import scorpion.controller : Controller, Get, Post, Put, Delete, Path, Pa
public import scorpion.entity : Entity;
public import scorpion.profile : Profile;
public import scorpion.service : Service, Repository, Where, OrderBy, Limit;
public import scorpion.session : Session, Authentication, Auth;
public import scorpion.session : Session, Authentication, Auth, AuthRedirect;
public import scorpion.validation : CustomValidation, NotEmpty, Min, Max, NotZero, Regex, Email, Optional, Validation;
public import scorpion.view : View, compile;

@@ -126,20 +126,33 @@ private class ControllerInfoImpl(T) : Info, ControllerInfo {
override void init(Router router, Config config, Database database) {
if(profiles.length == 0 || config.hasProfile(profiles)) {
T controller = new T();
static if(!__traits(compiles, getUDAs!(T, Controller)[0]())) auto controllerPath = getUDAs!(T, Controller)[0].path;
static if(!__traits(compiles, getUDAs!(T, Controller)[0]())) enum controllerPath = getUDAs!(T, Controller)[0].path;
foreach(immutable member ; __traits(allMembers, T)) {
static if(__traits(getProtection, __traits(getMember, T, member)) == "public") {
immutable full = "controller." ~ member;
static if(isFunction!(__traits(getMember, T, member))) {
foreach(immutable uda ; __traits(getAttributes, __traits(getMember, T, member))) {
static if(is(typeof(uda) == Route) || is(typeof(uda()) == Route)) {
static if(is(typeof(controllerPath))) auto path = controllerPath ~ uda.path;
else auto path = uda.path;
alias F = __traits(getMember, T, member);
auto fun = mixin(generateFunction!F(member));
info("Routing ", uda.method, " /", path.join("/"), " to ", T.stringof, ".", member);
router.add(routeInfo(uda.method, path.join(`\/`)), fun);
enum tests = {
string[] ret;
foreach(i, immutable uda; __traits(getAttributes, __traits(getMember, T, member))) {
static if(is(typeof(__traits(getMember, uda, "test")) == function)) {
static if(__traits(compiles, uda())) ret ~= "__traits(getAttributes, F)[" ~ i.to!string ~ "].init";
else ret ~= "__traits(getAttributes, F)[" ~ i.to!string ~ "]";
}
}
return ret;
}();
foreach(immutable uda ; getUDAs!(__traits(getMember, T, member), Route)) {
static if(__traits(compiles, { enum s=uda.path; })) {
static if(is(typeof(controllerPath))) enum path = controllerPath ~ uda.path;
else enum path = uda.path;
} else {
static if(is(typeof(controllerPath))) alias path = controllerPath;
else enum string[] path = [];
}
alias F = __traits(getMember, T, member);
auto fun = mixin(generateFunction!F(T.stringof, member, path.join(`\/`), tests));
router.add(routeInfo(uda.method, uda.hasBody, path.join(`\/`)), fun);
info("Routing ", uda.method, " /", path.join("/"), " to ", T.stringof, ".", member);
}
} else {
static if(hasUDA!(__traits(getMember, T, member), Init)) {
@@ -154,12 +167,16 @@ private class ControllerInfoImpl(T) : Info, ControllerInfo {
}
}

private static string generateFunction(alias M)(string member) {
private static string generateFunction(alias M)(string controller, string member, string path, string[] tests) {
string[] ret = ["ServerRequest request", "ServerResponse response"];
string body1 = "response.status=StatusCodes.ok;Validation validation=new Validation();";
string body1 = "response.headers[`X-Scorpion-Controller`]=`" ~ controller ~ "." ~ member ~ "`;response.headers[`X-Scorpion-Path`]=`" ~ path ~ "`;";
string body2;
string[Parameters!M.length] call;
bool validation = false;
foreach(test ; tests) {
body1 ~= "if(!" ~ test ~ ".test(request,response)){return;}";
}
body1 ~= "response.status=StatusCodes.ok;Validation validation=new Validation();";
foreach(i, param; Parameters!M) {
static if(is(param == ServerRequest)) call[i] = "request";
else static if(is(param == ServerResponse)) call[i] = "response";
@@ -1,34 +1,22 @@
module scorpion.session;

import std.algorithm : canFind;
import std.string : split, indexOf, strip;
import std.uuid : UUID, randomUUID, parseUUID, UUIDParsingException;

import lighttp : ServerRequest, ServerResponse;
import lighttp : ServerRequest, ServerResponse, StatusCodes, Cookie;

private enum cookieName = "__scorpion_ssid";
private enum cookieName = "__scorpion_session_id";

private Session[UUID] _sessions;

class Session {

public static Session get(ServerRequest request) {
auto cookies = "cookie" in request.headers;
if(cookies) {
foreach(cookie ; split(*cookies, ";")) {
immutable eq = cookie.indexOf("=");
if(eq > 0) {
immutable name = cookie[0..eq].strip;
if(name == cookieName) {
try {
auto ret = parseUUID(cookie[eq+1..$].strip) in _sessions;
if(ret is null) break;
return *ret;
} catch(UUIDParsingException) {
break;
}
}
}
}
if(auto cookie = cookieName in request.cookies) {
try {
if(auto ret = parseUUID(idup(*cookie)) in _sessions) return *ret;
} catch(UUIDParsingException) {}
}
return new Session();
}
@@ -45,7 +33,10 @@ class Session {

public void login(ServerResponse response, Authentication authentication) {
UUID uuid = randomUUID();
response.headers["Set-Cookie"] = cookieName ~ "=" ~ uuid.toString() ~ "; Path=/";
Cookie cookie = Cookie(cookieName, uuid.toString());
cookie.maxAge = 3600; // 1 hour
cookie.path = "/";
response.add(cookie);
_sessions[uuid] = this;
_authentication = authentication;
}
@@ -74,4 +65,37 @@ struct Auth {

string[] roles;

bool test(ServerRequest request, ServerResponse response) {
Session session = Session.get(request);
if(session.loggedIn) {
if(roles.length == 0) return true;
else foreach(role ; roles) {
if(session.authentication.roles.canFind(role)) return true;
}
}
response.status = StatusCodes.unauthorized;
return false;
}

}

struct AuthRedirect {

private string location;
private Auth _auth;

this(string location, string[] roles...) {
this.location = location;
_auth = Auth(roles);
}

bool test(ServerRequest request, ServerResponse response) {
if(!_auth.test(request, response)) {
response.redirect(StatusCodes.temporaryRedirect, location);
return false;
} else {
return true;
}
}

}
@@ -43,8 +43,12 @@ void start(string[] args) {
database = getDatabase(config, type);
database.connect(config.get("scorpion.database.name", string.init), config.get("scorpion.database.user", "root"), config.get("scorpion.database.password", ""));
}

ServerOptions options;
options.name = "Scorpion/~master";
options.max = config.get("scorpion.upload.max", 2 ^^ 24); // 16 MB

Server server = new Server("Scorpion");
Server server = new Server(options);
init(server.router, config, database);
server.host(ip, port);
server.run();
@@ -158,7 +158,7 @@ class Validation {

T validateParam(T)(string param, ServerRequest request, ServerResponse response) {
auto values = request.url.queryParams[param];
static if(isArray!T) {
static if(isArray!T && !is(T : string)) {
T ret;
foreach(value ; values) {
try {
@@ -49,3 +49,56 @@ class ScorpionAssetsController {
}

}

@Controller
class TestController {

@Auth
@Get("privet")
getAuth(Response response, Session session) {
response.body_ = "Welcome to the club, " ~ session.authentication.username;
}

@Auth("test")
@Get("privet2")
getAuth2(Response response, Session session) {
import std.string : join;
response.body_ = "You have a permission (" ~ session.authentication.roles.join(", ") ~ ")";
}

@Get("auth")
getAuthMe(Response response, Session session, @Param string username, @Param string[] roles) {
session.login(response, new A(username, roles));
}

@AuthRedirect("/")
@Get("redirect")
getRedirect(Response response) {
response.body_ = "HELLO";
}

class A : Authentication {

private string _username;
private string[] _roles;

this(string username, string[] roles) {
_username = username;
_roles = roles;
}

override uint userId() {
return 0;
}

override string username() {
return _username;
}

override string[] roles() {
return _roles;
}

}

}

0 comments on commit 5bcc525

Please sign in to comment.
You can’t perform that action at this time.