Skip to content

Commit

Permalink
#3477: decision endpoint should make decision based on anonymous user…
Browse files Browse the repository at this point in the history
…'s data when the user doesn't exist
  • Loading branch information
t83714 committed Aug 15, 2023
1 parent b7c6ed3 commit 701dcce
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 9 deletions.
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
- Move format `ESRI FEATURESERVER` under `csv-geo-au` in the default map preview format perference list
- Fixed: registry records editor UI doesn't encode long record ID correctly
- Provides HTTP header name information on user api key creation screen
- #3477: decision endpoint should make decision based on anonymous user's data when the user doesn't exist

## v2.2.5

Expand Down
14 changes: 10 additions & 4 deletions magda-authorization-api/src/createOpaRouter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,10 +120,16 @@ export default function createOpaRouter(options: OpaRouterOptions): Router {
}

async function appendUserInfoToInput(req: express.Request) {
const userInfo: User = await database.getCurrentUserInfo(
req,
jwtSecret
);
let userInfo: User;

try {
userInfo = await database.getCurrentUserInfo(req, jwtSecret);
} catch (e) {
console.error(
`Failed to get current user info for decision making: ${e}`
);
userInfo = await database.getDefaultAnonymousUserInfo();
}

let reqData: any = {};
const contentType = req.get("content-type");
Expand Down
81 changes: 77 additions & 4 deletions magda-authorization-api/src/test/createOpaRouter.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import Database from "../Database";
import mockApiKeyStore from "./mockApiKeyStore";
import { ANONYMOUS_USERS_ROLE_ID } from "magda-typescript-common/src/authorization-api/constants";
import testDataSimple from "magda-typescript-common/src/test/sampleOpaResponses/simple.json";
import buildJwt from "magda-typescript-common/src/session/buildJwt";

describe("Auth api router", function (this: Mocha.ISuiteCallbackContext) {
this.timeout(10000);
Expand Down Expand Up @@ -102,10 +103,6 @@ describe("Auth api router", function (this: Mocha.ISuiteCallbackContext) {
return app;
}

/*function setMockRequestSession(req: Request, userId: string) {
return req.set("X-Magda-Session", buildJwt(argv.jwtSecret, userId));
}*/

describe("Test `/decision`", () => {
it("should return 400 status code if not specify operation uri", async () => {
const scope = createOpaNockScope();
Expand Down Expand Up @@ -487,6 +484,82 @@ describe("Auth api router", function (this: Mocha.ISuiteCallbackContext) {
expect(query.pretty).to.be.equal("true");
expect(scope.isDone()).to.be.equal(true);
});

it("should making decisions using the correct users info based on singed JWT token carried in request header", async () => {
let data: any;

mockUserDataStore.reset();

const user = mockUserDataStore.createRecord({
displayName: "test user",
photoURL: "",
isAdmin: false,
email: "[email protected]",
source: "source_1",
sourceId: "source_id_1"
});

const scope = createOpaNockScope((queryParams, requestData) => {
data = requestData;
});
const req = request(app)
.get(`/decision/object/any-object/any-operation`)
.set("X-Magda-Session", buildJwt(argv.jwtSecret, user.id));

const res = await req;
// should be no error and return 200 status code
expect(res.ok).to.be.equal(true);
expect(res.body.hasResidualRules).to.be.equal(false);
expect(res.body.result).to.be.equal(false);
expect(scope.isDone()).to.be.equal(true);
expect(data.input.user.id).to.equal(user.id);
expect(data.input.user.email).to.equal(user.email);
expect(data.input.operationUri).to.equal(
"object/any-object/any-operation"
);
expect(data.input.resourceUri).to.equal("object/any-object");
expect(data.input.timestamp).to.be.within(
Date.now() - 20000,
Date.now() + 20000
);
});

it("should making decisions using anonymous users info when failed to retrieve the user info (user doesn't exist)", async () => {
let data: any;

mockUserDataStore.reset();

const scope = createOpaNockScope((queryParams, requestData) => {
data = requestData;
});
const req = request(app)
.get(`/decision/object/any-object/any-operation`)
.set(
"X-Magda-Session",
buildJwt(
argv.jwtSecret,
"ddd4a2ca-c536-45a9-8bee-eea21c630e4b"
)
);

const res = await req;
// should be no error and return 200 status code
expect(res.ok).to.be.equal(true);
expect(res.body.hasResidualRules).to.be.equal(false);
expect(res.body.result).to.be.equal(false);
expect(scope.isDone()).to.be.equal(true);
expect(
data.input.user.roles?.map((item: any) => item.id)
).to.have.members([ANONYMOUS_USERS_ROLE_ID]);
expect(data.input.operationUri).to.equal(
"object/any-object/any-operation"
);
expect(data.input.resourceUri).to.equal("object/any-object");
expect(data.input.timestamp).to.be.within(
Date.now() - 20000,
Date.now() + 20000
);
});
});

describe("Test `/decision` endpoint decision evlautation & encoding: ", () => {
Expand Down
18 changes: 17 additions & 1 deletion magda-authorization-api/src/test/mockDatabase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import Database from "../Database";
import NestedSetModelQueryer, { NodeRecord } from "../NestedSetModelQueryer";
import pg from "pg";
import mockApiKeyStore from "./mockApiKeyStore";
import { defaultAnonymousUserInfo } from "../Database";

export default class MockDatabase {
getUser(id: string): Promise<Maybe<User>> {
Expand Down Expand Up @@ -77,14 +78,29 @@ export default class MockDatabase {

check() {}

async getDefaultAnonymousUserInfo(): Promise<User> {
const user = { ...defaultAnonymousUserInfo };
try {
user.permissions = await this.getRolePermissions(user.roles[0].id);
user.roles[0].permissionIds = user.permissions.map(
(item) => item.id
);
return user;
} catch (e) {
return user;
}
}

async getCurrentUserInfo(req: any, jwtSecret: string): Promise<User> {
const db = sinon.createStubInstance(Database);
db.getUserPermissions.callsFake(this.getUserPermissions);
db.getRolePermissions.callsFake(this.getRolePermissions);
db.getUserRoles.callsFake(this.getUserRoles);
db.getUser.callsFake(this.getUser);
db.getCurrentUserInfo.callThrough();
db.getDefaultAnonymousUserInfo.callThrough();
db.getDefaultAnonymousUserInfo.callsFake(
this.getDefaultAnonymousUserInfo
);
return await db.getCurrentUserInfo(req, jwtSecret);
}

Expand Down

0 comments on commit 701dcce

Please sign in to comment.