Skip to content

Commit

Permalink
Merge pull request #433 from immobiliare/feat/added-oauth
Browse files Browse the repository at this point in the history
Feat: added oauth
  • Loading branch information
antoniomuso authored Apr 11, 2024
2 parents 0b0e956 + 22849c5 commit 33b3588
Show file tree
Hide file tree
Showing 9 changed files with 235 additions and 80 deletions.
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
- [Setup](#setup)
- [Setup Frontend Plugin](#setup-frontend-plugin)
- [Setup Backend Plugin](#setup-backend-plugin)
- [Extra OIDC/OAuth](#extra-oidcoauth)
- [Register To The New Backend System](#register-to-the-new-backend-system)
- [Annotations](#annotations)
- [Code owners file](#code-owners-file)
Expand Down Expand Up @@ -243,8 +244,23 @@ gitlab:
# This parameter controls SSL Certs verification
# Default: true
proxySecure: false
# Activate Oauth/OIDC
# Default: false
useOAuth: false
```

### Extra OIDC/OAuth

By default, this plugin utilizes the token specified in the configuration file `app-config.yaml` under the key: `integrations.gitlab[i].token`. However, you can opt out of using this token by activating OIDC as shown below:

```yaml
gitlab:
useOAuth: true
```

**Note:**: To use OIDC you have to configure the `@backstage/plugin-auth-backend-module-gitlab-provider` plugin.
**Note:**: OIDC does not allow multi GitLab instances!

### Register To The New Backend System

If you're already using the [New Backend System](https://backstage.io/docs/backend-system/), registering backend plugins will become much easier:
Expand Down
7 changes: 7 additions & 0 deletions packages/gitlab-backend/config.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,12 @@ export interface Config {
* @visibility backend
*/
proxySecure?: boolean;

/**
* Activate Oauth/OIDC
* @default "false"
* @visibility backend
*/
useOAuth?: boolean;
};
}
2 changes: 1 addition & 1 deletion packages/gitlab-backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@
"@backstage/backend-plugin-api": "^0.6.7",
"@backstage/config": "^1.1.1",
"@backstage/integration": "^1.7.0",
"@types/express": "*",
"body-parser": "^1.20.2",
"express": "^4.17.3",
"express-promise-router": "^4.1.0",
Expand All @@ -54,6 +53,7 @@
"@backstage/cli": "^0.22.8",
"@backstage/plugin-catalog-common": "^1.0.14",
"@backstage/plugin-catalog-node": "^1.3.7",
"@types/express": "^4.17.21",
"@types/supertest": "^2.0.12",
"msw": "^1.0.0",
"supertest": "^6.2.4"
Expand Down
184 changes: 131 additions & 53 deletions packages/gitlab-backend/src/service/router.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@ import { setupServer } from 'msw/node';

import { createRouter } from './router';

describe('createRouter', () => {
let app: express.Application;
const server = setupServer(
const buildServer = () => {
return setupServer(
rest.get(
'https://non-existing-example.com/api/v4/projects/434',
(req, res, ctx) => {
Expand Down Expand Up @@ -59,17 +58,27 @@ describe('createRouter', () => {
}
)
);
};

describe('createRouter', () => {
let app: express.Application;
const server = buildServer();

const token = 'Bearer iT6P7Ikla2zgBfGSPEWps';
const token2 = `${token}other`;

const config = new ConfigReader({
integrations: {
gitlab: [
{
host: 'non-existing-example.com',
apiBaseUrl: 'https://non-existing-example.com/api/v4',
token,
},
{
host: 'non-existing-example-2.com',
apiBaseUrl: 'https://non-existing-example-2.com/api/v4',
token: token2,
},
],
},
Expand Down Expand Up @@ -113,6 +122,7 @@ describe('createRouter', () => {
connection: 'close',
host: 'non-existing-example.com',
'user-agent': 'supertest',
'private-token': token,
},
url: 'https://non-existing-example.com/api/v4/projects/434',
});
Expand All @@ -132,6 +142,7 @@ describe('createRouter', () => {
connection: 'close',
host: 'non-existing-example-2.com',
'user-agent': 'supertest',
'private-token': token2,
},
url: 'https://non-existing-example-2.com/api/v4/projects/434',
});
Expand All @@ -155,6 +166,7 @@ describe('createRouter', () => {
'content-type': 'application/json',
host: 'non-existing-example.com',
'user-agent': 'supertest',
'private-token': token,
},
url: 'https://non-existing-example.com/api/graphql',
});
Expand All @@ -176,6 +188,7 @@ describe('createRouter', () => {
'content-type': 'application/json',
host: 'non-existing-example-2.com',
'user-agent': 'supertest',
'private-token': token2,
},
url: 'https://non-existing-example-2.com/api/graphql',
});
Expand Down Expand Up @@ -256,56 +269,7 @@ describe('createRouter', () => {

describe('createRouter with baseUrl', () => {
let app: express.Application;
const server = setupServer(
rest.get(
'https://non-existing-example.com/api/v4/projects/434',
(req, res, ctx) => {
return res(
ctx.status(200),
ctx.json({
url: req.url.toString(),
headers: req.headers.all(),
})
);
}
),
rest.get(
'https://non-existing-example-2.com/api/v4/projects/434',
(req, res, ctx) => {
return res(
ctx.status(200),
ctx.json({
url: req.url.toString(),
headers: req.headers.all(),
})
);
}
),
rest.post(
'https://non-existing-example.com/api/graphql',
(req, res, ctx) => {
return res(
ctx.status(200),
ctx.json({
url: req.url.toString(),
headers: req.headers.all(),
})
);
}
),
rest.post(
'https://non-existing-example-2.com/api/graphql',
(req, res, ctx) => {
return res(
ctx.status(200),
ctx.json({
url: req.url.toString(),
headers: req.headers.all(),
})
);
}
)
);
const server = buildServer();

const basePath = '/docs';

Expand Down Expand Up @@ -517,3 +481,117 @@ describe('createRouter with baseUrl', () => {
});
});
});

describe('OAuth token authorizations', () => {
let app: express.Application;
const OAuthToken = 'Bearer iT6P7Ikla2zgBfGSPEWps';
const server = setupServer(
rest.get(
'https://example-gitlab.com/api/v4/projects/434',
(req, res, ctx) => {
return res(
ctx.status(200),
ctx.json({
url: req.url.toString(),
headers: req.headers.all(),
})
);
}
),
rest.post('https://example-gitlab.com/api/graphql', (req, res, ctx) => {
return res(
ctx.status(200),
ctx.json({
url: req.url.toString(),
headers: req.headers.all(),
})
);
})
);

const config = new ConfigReader({
gitlab: {
useOAuth: true,
},
integrations: {
gitlab: [
{
host: 'example-gitlab.com',
apiBaseUrl: 'https://example-gitlab.com/api/v4',
token: 'Bearer different-from-oauth',
},
],
},
});

beforeAll(async () => {
const router = await createRouter({
logger: getVoidLogger(),
config,
});
app = express().use('/api/gitlab', router);
server.listen({
onUnhandledRequest: ({ headers }, print) => {
if (headers.get('User-Agent') === 'supertest') {
return;
}
print.error();
},
});
});

afterAll(() => server.close());

beforeEach(async () => {
jest.resetAllMocks();
server.resetHandlers();
});

describe('GET Request', () => {
it('Oauth Token should work', async () => {
const agent = request.agent(app);
// this is set to let msw pass test requests through the mock server
agent.set('User-Agent', 'supertest');
agent.set('gitlab-authorization', OAuthToken);
const response = await agent.get(
'/api/gitlab/rest/example-gitlab.com/projects/434'
);
expect(response.status).toEqual(200);
expect(response.body).toEqual({
headers: {
'accept-encoding': 'gzip, deflate',
connection: 'close',
host: 'example-gitlab.com',
'user-agent': 'supertest',
authorization: OAuthToken,
},
url: 'https://example-gitlab.com/api/v4/projects/434',
});
});
});

describe('Graphql requests', () => {
it('Oauth Token should work', async () => {
const agent = request.agent(app);
// this is set to let msw pass test requests through the mock server
agent.set('User-Agent', 'supertest');
agent.set('gitlab-authorization', OAuthToken);
const response = await agent.post(
'/api/gitlab/graphql/example-gitlab.com'
);
expect(response.status).toEqual(200);
expect(response.body).toEqual({
headers: {
'accept-encoding': 'gzip, deflate',
connection: 'close',
'content-length': '2',
'content-type': 'application/json',
host: 'example-gitlab.com',
'user-agent': 'supertest',
authorization: OAuthToken,
},
url: 'https://example-gitlab.com/api/graphql',
});
});
});
});
21 changes: 19 additions & 2 deletions packages/gitlab-backend/src/service/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export async function createRouter(
): Promise<express.Router> {
const { logger, config } = options;
const secure = config.getOptionalBoolean('gitlab.proxySecure');
const useOAuth = config.getOptionalBoolean('gitlab.useOAuth');
const basePath = getBasePath(config) || '';

const gitlabIntegrations: GitLabIntegrationConfig[] =
Expand All @@ -49,11 +50,25 @@ export async function createRouter(
// target, causing a ERR_HTTP_HEADERS_SENT crash
const filter = (_pathname: string, req: Request): boolean => {
if (req.headers['authorization']) delete req.headers['authorization'];
// Forward authorization, this header is defined when gitlab.useOAuth is true
if (req.headers['gitlab-authorization']) {
req.headers['authorization'] = req.headers[
'gitlab-authorization'
] as string;
delete req.headers['gitlab-authorization'];
}
return req.method === 'GET';
};

const graphqlFilter = (_pathname: string, req: Request): boolean => {
if (req.headers['authorization']) delete req.headers['authorization'];
// Forward authorization, this header is defined when gitlab.useOAuth is true
if (req.headers['gitlab-authorization']) {
req.headers['authorization'] = req.headers[
'gitlab-authorization'
] as string;
delete req.headers['gitlab-authorization'];
}
return req.method === 'POST' && !req.body.query?.includes('mutation');
};

Expand All @@ -66,7 +81,8 @@ export async function createRouter(
target: apiUrl.origin,
changeOrigin: true,
headers: {
...(token ? { 'PRIVATE-TOKEN': token } : {}),
// If useOAuth is true, we don't not add the token
...(token && !useOAuth ? { 'PRIVATE-TOKEN': token } : {}),
},
secure,
onProxyReq: (proxyReq, req) => {
Expand Down Expand Up @@ -96,7 +112,8 @@ export async function createRouter(
target: apiUrl.origin,
changeOrigin: true,
headers: {
...(token ? { 'PRIVATE-TOKEN': token } : {}),
// If useOAuth is true, we don't not add the token
...(token && !useOAuth ? { 'PRIVATE-TOKEN': token } : {}),
},
secure,
logProvider: () => logger,
Expand Down
7 changes: 7 additions & 0 deletions packages/gitlab/config.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,12 @@ export interface Config {
* @visibility frontend
*/
defaultReadmePath?: string;

/**
* Activate Oauth/OIDC
* @default "false"
* @visibility frontend
*/
useOAuth?: boolean;
};
}
Loading

0 comments on commit 33b3588

Please sign in to comment.