chore: added some additional scopes support for the ScmAuthApi

Signed-off-by: blam <ben@blam.sh>
This commit is contained in:
blam
2022-01-25 15:17:04 +01:00
parent aa4be23eaf
commit 2e5c4d1a15
3 changed files with 102 additions and 8 deletions
@@ -141,6 +141,58 @@ describe('ScmAuth', () => {
});
});
it('should support additional provided scopes from the caller', async () => {
const mockAuthApi = {
getAccessToken: async (scopes: string[]) => {
return scopes.join(' ');
},
};
const githubAuth = ScmAuth.forGithub(mockAuthApi);
await expect(
githubAuth.getCredentials({
url: 'http://example.com',
additionalScope: {
customScopes: { github: ['org:read', 'workflow:write'] },
},
}),
).resolves.toMatchObject({
token: 'repo read:org read:user org:read workflow:write',
});
const gitlabAuth = ScmAuth.forGitlab(mockAuthApi);
await expect(
gitlabAuth.getCredentials({
url: 'http://example.com',
additionalScope: { customScopes: { gitlab: ['write_repository'] } },
}),
).resolves.toMatchObject({
token: 'read_user read_api read_repository write_repository',
});
const azureAuth = ScmAuth.forAzure(mockAuthApi);
await expect(
azureAuth.getCredentials({
url: 'http://example.com',
additionalScope: { customScopes: { azure: ['vso.org'] } },
}),
).resolves.toMatchObject({
token: 'vso.build vso.code vso.graph vso.project vso.profile vso.org',
});
const bitbucketAuth = ScmAuth.forBitbucket(mockAuthApi);
await expect(
bitbucketAuth.getCredentials({
url: 'http://example.com',
additionalScope: {
customScopes: { bitbucket: ['snippet:write', 'issue:write'] },
},
}),
).resolves.toMatchObject({
token: 'account team pullrequest snippet issue snippet:write issue:write',
});
});
it('should handle host option', () => {
const mockAuthApi = {
getAccessToken: jest.fn(),
+40 -8
View File
@@ -35,6 +35,9 @@ type ScopeMapping = {
repoWrite: string[];
};
// An enum of all supported providers
type ProviderName = 'generic' | 'github' | 'azure' | 'bitbucket' | 'gitlab';
class ScmAuthMux implements ScmAuthApi {
#providers: Array<ScmAuth>;
@@ -93,12 +96,14 @@ export class ScmAuth implements ScmAuthApi {
github: githubAuthApiRef,
gitlab: gitlabAuthApiRef,
azure: microsoftAuthApiRef,
bitbucket: bitbucketAuthApiRef,
},
factory: ({ github, gitlab, azure }) =>
factory: ({ github, gitlab, azure, bitbucket }) =>
ScmAuth.merge(
ScmAuth.forGithub(github),
ScmAuth.forGitlab(gitlab),
ScmAuth.forAzure(azure),
ScmAuth.forBitbucket(bitbucket),
),
});
}
@@ -116,7 +121,7 @@ export class ScmAuth implements ScmAuthApi {
};
},
): ScmAuth {
return new ScmAuth(authApi, options.host, options.scopeMapping);
return new ScmAuth('generic', authApi, options.host, options.scopeMapping);
}
/**
@@ -139,7 +144,7 @@ export class ScmAuth implements ScmAuthApi {
},
): ScmAuth {
const host = options?.host ?? 'github.com';
return new ScmAuth(githubAuthApi, host, {
return new ScmAuth('github', githubAuthApi, host, {
default: ['repo', 'read:org', 'read:user'],
repoWrite: ['gist'],
});
@@ -165,7 +170,7 @@ export class ScmAuth implements ScmAuthApi {
},
): ScmAuth {
const host = options?.host ?? 'gitlab.com';
return new ScmAuth(gitlabAuthApi, host, {
return new ScmAuth('gitlab', gitlabAuthApi, host, {
default: ['read_user', 'read_api', 'read_repository'],
repoWrite: ['write_repository', 'api'],
});
@@ -191,7 +196,7 @@ export class ScmAuth implements ScmAuthApi {
},
): ScmAuth {
const host = options?.host ?? 'dev.azure.com';
return new ScmAuth(microsoftAuthApi, host, {
return new ScmAuth('azure', microsoftAuthApi, host, {
default: [
'vso.build',
'vso.code',
@@ -223,7 +228,7 @@ export class ScmAuth implements ScmAuthApi {
},
): ScmAuth {
const host = options?.host ?? 'bitbucket.org';
return new ScmAuth(bitbucketAuthApi, host, {
return new ScmAuth('bitbucket', bitbucketAuthApi, host, {
default: ['account', 'team', 'pullrequest', 'snippet', 'issue'],
repoWrite: ['pullrequest:write', 'snippet:write', 'issue:write'],
});
@@ -240,11 +245,18 @@ export class ScmAuth implements ScmAuthApi {
#api: OAuthApi;
#host: string;
#scopeMapping: ScopeMapping;
#providerName: ProviderName;
private constructor(api: OAuthApi, host: string, scopeMapping: ScopeMapping) {
private constructor(
providerName: ProviderName,
api: OAuthApi,
host: string,
scopeMapping: ScopeMapping,
) {
this.#api = api;
this.#host = host;
this.#scopeMapping = scopeMapping;
this.#providerName = providerName;
}
/**
@@ -254,6 +266,16 @@ export class ScmAuth implements ScmAuthApi {
return url.host === this.#host;
}
private getAdditionalScopesForProvider(
additionalScopes: ScmAuthTokenOptions['additionalScope'],
): string[] {
if (!additionalScopes?.customScopes || this.#providerName === 'generic') {
return [];
}
return additionalScopes.customScopes?.[this.#providerName] ?? [];
}
/**
* Fetches credentials for the given resource.
*/
@@ -267,7 +289,17 @@ export class ScmAuth implements ScmAuthApi {
scopes.push(...this.#scopeMapping.repoWrite);
}
const token = await this.#api.getAccessToken(scopes, restOptions);
const additionalScopes =
this.getAdditionalScopesForProvider(additionalScope);
if (additionalScopes.length) {
scopes.push(...additionalScopes);
}
const uniqueScopes = [...new Set(scopes)];
const token = await this.#api.getAccessToken(uniqueScopes, restOptions);
return {
token,
headers: {
@@ -44,6 +44,16 @@ export interface ScmAuthTokenOptions extends AuthRequestOptions {
* the ability to create things like issues and pull requests.
*/
repoWrite?: boolean;
/**
* Allow an arbitrary list of scopes provided from the user
* to request from the provider.
*/
customScopes?: {
github?: string[];
azure?: string[];
bitbucket?: string[];
gitlab?: string[];
};
};
}