Fixes issue with organization name case sensitivity when using allowedInstallationOwners

Signed-off-by: Lee Standen <lee.standen@ironcladhq.com>
This commit is contained in:
Lee Standen
2025-09-10 15:57:16 -07:00
parent 8d18d23e34
commit 56897d717e
3 changed files with 49 additions and 2 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/integration': patch
---
Fixes issue with Github credentials provider which fails to match organization name if using allowedInstallationOwners
@@ -231,6 +231,42 @@ describe('SingleInstanceGithubCredentialsProvider tests', () => {
expect(token).toEqual(undefined);
});
it('should not fail to issue tokens for an organization when there is a case mismatch in the organization name', async () => {
octokit.apps.listInstallations.mockResolvedValue({
headers: {
etag: '123',
},
data: [
{
id: 1,
repository_selection: 'selected',
account: {
login: 'backstage',
},
},
],
} as RestEndpointMethodTypes['apps']['listInstallations']['response']);
octokit.apps.createInstallationAccessToken.mockResolvedValueOnce({
data: {
expires_at: DateTime.local().plus({ hours: 1 }).toString(),
token: 'secret_token',
repository_selection: 'selected',
},
} as RestEndpointMethodTypes['apps']['createInstallationAccessToken']['response']);
octokit.apps.listReposAccessibleToInstallation.mockReturnValue({
data: [{ name: 'some-repo' }],
} as unknown as RestEndpointMethodTypes['apps']['listReposAccessibleToInstallation']['response']);
const { token, headers } = await github.getCredentials({
url: 'https://github.com/Backstage',
});
const expectedToken = 'secret_token';
expect(headers).toEqual({ Authorization: `Bearer ${expectedToken}` });
expect(token).toEqual('secret_token');
});
it('should not fail to issue tokens for an organization when the app is installed for a single repo', async () => {
octokit.apps.listInstallations.mockResolvedValue({
headers: {
@@ -102,7 +102,9 @@ class GithubAppManager {
private readonly allowedInstallationOwners: string[] | undefined; // undefined allows all installations
constructor(config: GithubAppConfig, baseUrl?: string) {
this.allowedInstallationOwners = config.allowedInstallationOwners;
this.allowedInstallationOwners = config.allowedInstallationOwners?.map(
owner => owner.toLocaleLowerCase('en-US'),
);
this.baseUrl = baseUrl;
this.baseAuthConfig = {
appId: config.appId,
@@ -121,7 +123,11 @@ class GithubAppManager {
repo?: string,
): Promise<{ accessToken: string | undefined }> {
if (this.allowedInstallationOwners) {
if (!this.allowedInstallationOwners?.includes(owner)) {
if (
!this.allowedInstallationOwners?.includes(
owner.toLocaleLowerCase('en-US'),
)
) {
return { accessToken: undefined }; // An empty token allows anonymous access to public repos
}
}