Skip to content

Commit

Permalink
Introduce Subject and Object in authorization module.
Browse files Browse the repository at this point in the history
  • Loading branch information
clems4ever committed Nov 17, 2018
1 parent 97bfafb commit b53d16d
Show file tree
Hide file tree
Showing 11 changed files with 130 additions and 125 deletions.
180 changes: 90 additions & 90 deletions server/src/lib/authorization/Authorizer.spec.ts

Large diffs are not rendered by default.

20 changes: 11 additions & 9 deletions server/src/lib/authorization/Authorizer.ts
Expand Up @@ -4,6 +4,8 @@ import { IAuthorizer } from "./IAuthorizer";
import { Winston } from "../../../types/Dependencies";
import { MultipleDomainMatcher } from "./MultipleDomainMatcher";
import { Level } from "./Level";
import { Object } from "./Object";
import { Subject } from "./Subject";

function MatchDomain(actualDomain: string) {
return function (rule: ACLRule): boolean {
Expand All @@ -24,19 +26,19 @@ function MatchResource(actualResource: string) {
};
}

function MatchSubject(user: string, groups: string[]) {
function MatchSubject(subject: Subject) {
return (rule: ACLRule) => {
// If no subject, matches anybody
if (!rule.subject) return true;

if (rule.subject.startsWith("user:")) {
const ruleUser = rule.subject.split(":")[1];
if (user == ruleUser) return true;
if (subject.user == ruleUser) return true;
}

if (rule.subject.startsWith("group:")) {
const ruleGroup = rule.subject.split(":")[1];
if (groups.indexOf(ruleGroup) > -1) return true;
if (subject.groups.indexOf(ruleGroup) > -1) return true;
}
return false;
};
Expand All @@ -51,13 +53,13 @@ export class Authorizer implements IAuthorizer {
this.configuration = configuration;
}

private getMatchingRules(domain: string, resource: string, user: string, groups: string[]): ACLRule[] {
private getMatchingRules(object: Object, subject: Subject): ACLRule[] {
const rules = this.configuration.rules;
if (!rules) return [];
return rules
.filter(MatchDomain(domain))
.filter(MatchResource(resource))
.filter(MatchSubject(user, groups));
.filter(MatchDomain(object.domain))
.filter(MatchResource(object.resource))
.filter(MatchSubject(subject));
}

private ruleToLevel(policy: string): Level {
Expand All @@ -71,10 +73,10 @@ export class Authorizer implements IAuthorizer {
return Level.DENY;
}

authorization(domain: string, resource: string, user: string, groups: string[]): Level {
authorization(object: Object, subject: Subject): Level {
if (!this.configuration) return Level.BYPASS;

const rules = this.getMatchingRules(domain, resource, user, groups);
const rules = this.getMatchingRules(object, subject);

return (rules.length > 0)
? this.ruleToLevel(rules[0].policy) // extract the policy of the first matching rule
Expand Down
6 changes: 4 additions & 2 deletions server/src/lib/authorization/AuthorizerStub.spec.ts
@@ -1,6 +1,8 @@
import Sinon = require("sinon");
import { IAuthorizer } from "./IAuthorizer";
import { Level } from "./Level";
import { Object } from "./Object";
import { Subject } from "./Subject";

export class AuthorizerStub implements IAuthorizer {
authorizationMock: Sinon.SinonStub;
Expand All @@ -9,7 +11,7 @@ export class AuthorizerStub implements IAuthorizer {
this.authorizationMock = Sinon.stub();
}

authorization(domain: string, resource: string, user: string, groups: string[]): Level {
return this.authorizationMock(domain, resource, user, groups);
authorization(object: Object, subject: Subject): Level {
return this.authorizationMock(object, subject);
}
}
4 changes: 3 additions & 1 deletion server/src/lib/authorization/IAuthorizer.ts
@@ -1,5 +1,7 @@
import { Level } from "./Level";
import { Subject } from "./Subject";
import { Object } from "./Object";

export interface IAuthorizer {
authorization(domain: string, resource: string, user: string, groups: string[]): Level;
authorization(object: Object, subject: Subject): Level;
}
5 changes: 5 additions & 0 deletions server/src/lib/authorization/Object.ts
@@ -0,0 +1,5 @@

export interface Object {
domain: string;
resource: string;
}
5 changes: 5 additions & 0 deletions server/src/lib/authorization/Subject.ts
@@ -0,0 +1,5 @@

export interface Subject {
user: string;
groups: string[];
}
3 changes: 2 additions & 1 deletion server/src/lib/routes/firstfactor/post.ts
Expand Up @@ -59,7 +59,8 @@ export default function (vars: ServerVariables) {
const decomposition = URLDecomposer.fromUrl(redirectUrl);
const authorizationLevel = (decomposition)
? vars.authorizer.authorization(
decomposition.domain, decomposition.path, username, groups)
{domain: decomposition.domain, resource: decomposition.path},
{user: username, groups: groups})
: AuthorizationLevel.TWO_FACTOR;

if (emails.length > 0)
Expand Down
10 changes: 5 additions & 5 deletions server/src/lib/routes/verify/access_control.ts
Expand Up @@ -28,22 +28,22 @@ function isAuthorized(
export default function (
req: Express.Request,
vars: ServerVariables,
domain: string, path: string,
username: string, groups: string[],
domain: string, resource: string,
user: string, groups: string[],
authenticationLevel: AuthenticationLevel) {

return new BluebirdPromise(function (resolve, reject) {
const authorizationLevel = vars.authorizer
.authorization(domain, path, username, groups);
.authorization({domain, resource}, {user, groups});

if (!isAuthorized(authorizationLevel, authenticationLevel)) {
if (authorizationLevel == AuthorizationLevel.DENY) {
reject(new Exceptions.NotAuthorizedError(
Util.format("User %s is unauthorized to access %s%s", username, domain, path)));
Util.format("User %s is not authorized to access %s%s", user, domain, resource)));
return;
}
reject(new Exceptions.NotAuthenticatedError(Util.format(
"User '%s' is not sufficiently authenticated.", username, domain, path)));
"User '%s' is not sufficiently authorized to access %s%s.", user, domain, resource)));
return;
}
resolve();
Expand Down
5 changes: 0 additions & 5 deletions server/src/lib/routes/verify/get_basic_auth.ts
Expand Up @@ -4,11 +4,6 @@ import ObjectPath = require("object-path");
import { ServerVariables } from "../../ServerVariables";
import { AuthenticationSession }
from "../../../../types/AuthenticationSession";
<<<<<<< HEAD
import { DomainExtractor } from "../../../../../shared/DomainExtractor";
import { MethodCalculator } from "../../authentication/MethodCalculator";
=======
>>>>>>> Integrate more policy options in ACL rules.
import AccessControl from "./access_control";
import { URLDecomposer } from "../../utils/URLDecomposer";
import { Level } from "../../authentication/Level";
Expand Down
12 changes: 5 additions & 7 deletions server/src/lib/routes/verify/get_session_cookie.ts
Expand Up @@ -14,9 +14,6 @@ import { AuthenticationSessionHandler }
import AccessControl from "./access_control";
import { URLDecomposer } from "../../utils/URLDecomposer";

const FIRST_FACTOR_NOT_VALIDATED_MESSAGE = "First factor not yet validated";
const SECOND_FACTOR_NOT_VALIDATED_MESSAGE = "Second factor not yet validated";

function verify_inactivity(req: Express.Request,
authSession: AuthenticationSession,
configuration: Configuration, logger: IRequestLogger)
Expand Down Expand Up @@ -54,18 +51,19 @@ export default function (req: Express.Request, res: Express.Response,

if (!authSession.userid) {
return BluebirdPromise.reject(new Exceptions.AccessDeniedError(
Util.format("%s: %s.", FIRST_FACTOR_NOT_VALIDATED_MESSAGE,
"userid is missing")));
"userid is missing"));
}

const originalUrl = ObjectPath.get<Express.Request, string>(req, "headers.x-original-url");
const originalUrl = ObjectPath.get<Express.Request, string>(
req, "headers.x-original-url");
const originalUri =
ObjectPath.get<Express.Request, string>(req, "headers.x-original-uri");

const d = URLDecomposer.fromUrl(originalUrl);
vars.logger.debug(req, "domain=%s, path=%s, user=%s, groups=%s", d.domain,
d.path, username, groups.join(","));
return AccessControl(req, vars, d.domain, d.path, username, groups, authSession.authentication_level);
return AccessControl(req, vars, d.domain, d.path, username, groups,
authSession.authentication_level);
})
.then(() => {
return verify_inactivity(req, authSession,
Expand Down
5 changes: 0 additions & 5 deletions test/features/step_definitions/restrictions.ts
Expand Up @@ -12,11 +12,6 @@ Then("I get an error {int}", function (code: number) {
return this.getErrorPage(code);
});

When("I request {string} with method {string}",
function (url: string, method: string) {
const that = this;
});

function requestAndExpectStatusCode(ctx: any, url: string, method: string,
expectedStatusCode: number) {
return Request(url, {
Expand Down

0 comments on commit b53d16d

Please sign in to comment.