Skip to content

Commit cebc8ff

Browse files
committed
[auth] Add tests for BearerAuth.authExpressRequest
1 parent 8a86de0 commit cebc8ff

File tree

2 files changed

+126
-12
lines changed

2 files changed

+126
-12
lines changed
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
/**
2+
* Copyright (c) 2022 Gitpod GmbH. All rights reserved.
3+
* Licensed under the GNU Affero General Public License (AGPL).
4+
* See License.AGPL.txt in the project root for license information.
5+
*/
6+
7+
import { TypeORM, resetDB } from "@gitpod/gitpod-db/lib";
8+
import { BearerAuth, PersonalAccessToken } from "./bearer-authenticator";
9+
import { expect } from "chai";
10+
import { describe } from "mocha";
11+
import { Container } from "inversify";
12+
import { createTestContainer } from "../test/service-testing-container-module";
13+
import { Experiments } from "@gitpod/gitpod-protocol/lib/experiments/configcat-server";
14+
import { UserService } from "../user/user-service";
15+
import { User } from "@gitpod/gitpod-protocol";
16+
import { Config } from "../config";
17+
import { Request } from "express";
18+
import { WithResourceAccessGuard } from "./resource-access";
19+
import { WithFunctionAccessGuard } from "./function-access";
20+
import { fail } from "assert";
21+
22+
function toDateTime(date: Date): string {
23+
return date.toISOString().replace("T", " ").replace("Z", "");
24+
}
25+
26+
describe("BearerAuth", () => {
27+
let container: Container;
28+
let bearerAuth: BearerAuth;
29+
let userService: UserService;
30+
let typeORM: TypeORM;
31+
let testUser: User;
32+
33+
async function insertPat(userId: string, patId: string, scopes: string[] = ["function:*"]): Promise<string> {
34+
const patValue = "someValue";
35+
const signature = "V7BsZVjpMQRaWxS5XJE9r-Ovpxk2xT_bfFSmic4yW6g"; // depends on the value
36+
const pat = new PersonalAccessToken("doesnotmatter", patValue);
37+
38+
const conn = await typeORM.getConnection();
39+
await conn.query(
40+
"INSERT d_b_personal_access_token (id, userId, hash, name, scopes, expirationTime, createdAt) VALUES (?, ?, ?, ?, ?, ?, ?)",
41+
[patId, userId, pat.hash(), patId, scopes, toDateTime(new Date("2030")), toDateTime(new Date())],
42+
);
43+
return `gitpod_pat_${signature}.${patValue}`;
44+
}
45+
46+
beforeEach(async () => {
47+
container = createTestContainer();
48+
Experiments.configureTestingClient({
49+
centralizedPermissions: true,
50+
});
51+
const oldConfig = container.get<Config>(Config);
52+
container.rebind(Config).toDynamicValue((ctx) => {
53+
return {
54+
...oldConfig,
55+
patSigningKey: "super-duper-secret-pat-signing-key",
56+
};
57+
});
58+
bearerAuth = container.get(BearerAuth);
59+
userService = container.get<UserService>(UserService);
60+
typeORM = container.get<TypeORM>(TypeORM);
61+
62+
testUser = await userService.createUser({
63+
identity: {
64+
authId: "gh-user-1",
65+
authName: "user",
66+
authProviderId: "public-github",
67+
},
68+
});
69+
});
70+
71+
afterEach(async () => {
72+
// Clean-up database
73+
await resetDB(container.get(TypeORM));
74+
});
75+
76+
it("authExpressRequest should successfully authenticate BearerToken (PAT)", async () => {
77+
const pat1 = await insertPat(testUser.id, "pat-1");
78+
79+
const req = {
80+
headers: {
81+
authorization: `Bearer ${pat1}`,
82+
},
83+
} as Request;
84+
await bearerAuth.authExpressRequest(req);
85+
86+
expect(req.user?.id).to.equal(testUser.id);
87+
expect((req as WithResourceAccessGuard).resourceGuard).to.not.be.undefined;
88+
expect((req as WithFunctionAccessGuard).functionGuard).to.not.be.undefined;
89+
});
90+
91+
it("authExpressRequest should fail to authenticate with missing BearerToken in header", async () => {
92+
await insertPat(testUser.id, "pat-1");
93+
94+
const req = {
95+
headers: {
96+
authorization: `Bearer `, // missing
97+
},
98+
} as Request;
99+
await expectError(async () => bearerAuth.authExpressRequest(req), "missing bearer token header");
100+
});
101+
102+
it("authExpressRequest should fail to authenticate with missing BearerToken from DB (PAT)", async () => {
103+
const patNotStored = "gitpod_pat_GrvGthczSRf3ypqFhNtcRiN5fK6CV7rdCkkPLfpbc_4";
104+
105+
const req = {
106+
headers: {
107+
authorization: `Bearer ${patNotStored}`,
108+
},
109+
} as Request;
110+
await expectError(async () => bearerAuth.authExpressRequest(req), "cannot find token");
111+
});
112+
113+
async function expectError(fun: () => Promise<any>, message: string) {
114+
try {
115+
await fun();
116+
fail(`Expected error: ${message}`);
117+
} catch (err) {}
118+
}
119+
});

components/server/src/auth/bearer-authenticator.spec.ts renamed to components/server/src/auth/personal-access-token.spec.ts

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,20 @@
44
* See License.AGPL.txt in the project root for license information.
55
*/
66

7-
import { suite, test } from "@testdeck/mocha";
87
import { PersonalAccessToken } from "./bearer-authenticator";
98
import { expect } from "chai";
9+
import { describe } from "mocha";
1010

11-
@suite()
12-
class TestPersonalAccessToken {
13-
@test
14-
test_parse_token_ok() {
11+
describe("PersonalAccessToken", () => {
12+
it("should parse token successfully", () => {
1513
const token = "gitpod_pat_GrvGthczSRf3ypqFhNtcRiN5fK6CV7rdCkkPLfpbc_4." + "test".repeat(10);
1614

1715
const parsed = PersonalAccessToken.parse(token);
1816
const expected = new PersonalAccessToken("GrvGthczSRf3ypqFhNtcRiN5fK6CV7rdCkkPLfpbc_4", "test".repeat(10));
1917
expect(parsed).to.deep.equal(expected);
20-
}
18+
});
2119

22-
@test
23-
test_parse_token_throws() {
20+
it("should parse token and throw an error", () => {
2421
const tokens = [
2522
"gitpod_pat_GrvGthczSRf3ypqFhNtcRiN5fK6CV7rdCkkPLfpbc_4.", // no value
2623
`gitpod_pat_.${"test".repeat(10)}`, // no signature
@@ -30,7 +27,5 @@ class TestPersonalAccessToken {
3027
tokens.forEach((token) => {
3128
expect(() => PersonalAccessToken.parse(token)).to.throw();
3229
});
33-
}
34-
}
35-
36-
module.exports = new TestPersonalAccessToken();
30+
});
31+
});

0 commit comments

Comments
 (0)