mirror of
https://github.com/renovatebot/renovate.git
synced 2026-11-01 18:19:51 +00:00
7a6e7b2880
* implement plan * add tests * fix initialization order * cast contexts to WebApi for type safety in authHandler checks * fix(azure): scope PR filtering to effective credentials (#1) * fix(azure): scope PR filtering to effective credentials * fix(azure): address authentication review feedback * fix: reorder import statements * Address review comments * fetch renovate user id in initPlatform --------- Co-authored-by: Sergei Zharinov <zharinov@users.noreply.github.com>
106 lines
3.7 KiB
TypeScript
106 lines
3.7 KiB
TypeScript
import * as azure from 'azure-devops-node-api';
|
|
import {
|
|
getBasicHandler,
|
|
getBearerHandler,
|
|
getPersonalAccessTokenHandler,
|
|
} from 'azure-devops-node-api';
|
|
import type { ICoreApi } from 'azure-devops-node-api/CoreApi.js';
|
|
import type { IGitApi } from 'azure-devops-node-api/GitApi.js';
|
|
import { DeploymentFlags } from 'azure-devops-node-api/interfaces/common/VSSInterfaces.js';
|
|
import type { IRequestHandler } from 'azure-devops-node-api/interfaces/common/VsoBaseInterfaces.js';
|
|
import type { IPolicyApi } from 'azure-devops-node-api/PolicyApi.js';
|
|
import type { IWorkItemTrackingApi } from 'azure-devops-node-api/WorkItemTrackingApi.js';
|
|
import { logger } from '../../../logger/index.ts';
|
|
import type { HostRule } from '../../../types/index.ts';
|
|
import * as hostRules from '../../../util/host-rules.ts';
|
|
import { isProbablyJwt } from '../../../util/http/jwt.ts';
|
|
|
|
const hostType = 'azure';
|
|
let endpoint: string;
|
|
|
|
function getAuthenticationHandler(config: HostRule): IRequestHandler {
|
|
if (!config.token && config.username && config.password) {
|
|
return getBasicHandler(config.username, config.password, true);
|
|
}
|
|
if (config.token && isProbablyJwt(config.token)) {
|
|
logger.debug('Using Bearer authentication (JWT detected)');
|
|
return getBearerHandler(config.token, true);
|
|
}
|
|
logger.debug('Using PAT authentication');
|
|
// TODO: token can be undefined here (#22198)
|
|
return getPersonalAccessTokenHandler(config.token!, true);
|
|
}
|
|
|
|
export function azureObj(credentials?: HostRule): azure.WebApi {
|
|
const config = credentials ?? hostRules.find({ hostType, url: endpoint });
|
|
if (!config.token && !(config.username && config.password)) {
|
|
throw new Error(`No config found for azure`);
|
|
}
|
|
const authHandler = getAuthenticationHandler(config);
|
|
return new azure.WebApi(endpoint, authHandler, {
|
|
allowRetries: true,
|
|
maxRetries: 2,
|
|
});
|
|
}
|
|
|
|
export function gitApi(credentials?: HostRule): Promise<IGitApi> {
|
|
return azureObj(credentials).getGitApi();
|
|
}
|
|
|
|
export function coreApi(): Promise<ICoreApi> {
|
|
return azureObj().getCoreApi();
|
|
}
|
|
|
|
export function policyApi(): Promise<IPolicyApi> {
|
|
return azureObj().getPolicyApi();
|
|
}
|
|
|
|
export function workItemTrackingApi(): Promise<IWorkItemTrackingApi> {
|
|
return azureObj().getWorkItemTrackingApi();
|
|
}
|
|
|
|
export async function getAuthenticatedUserId(
|
|
credentials: HostRule,
|
|
): Promise<string | undefined> {
|
|
try {
|
|
const { authenticatedUser } = await azureObj(credentials).connect();
|
|
if (!authenticatedUser?.id) {
|
|
logger.debug('Azure: authenticated user ID is unavailable');
|
|
}
|
|
return authenticatedUser?.id;
|
|
} catch (err) {
|
|
logger.debug({ err }, 'Azure: could not determine authenticated user ID');
|
|
return undefined;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Whether the endpoint is Azure DevOps Services (cloud) rather than Azure
|
|
* DevOps Server (on-premises). Read from the location service's
|
|
* `_apis/connectionData`, which reports the deployment type authoritatively.
|
|
* Defaults to `false` (on-premises) when the type cannot be determined, so
|
|
* callers stay on the behaviour that both products support.
|
|
*/
|
|
export async function isHosted(): Promise<boolean> {
|
|
try {
|
|
const { deploymentType } = await azureObj().connect();
|
|
// `connect()` returns the response body as-is, without running the SDK
|
|
// deserializer, so enums arrive as their name (`hosted`) instead of their
|
|
// numeric value. Accept both forms.
|
|
return (
|
|
deploymentType === DeploymentFlags.Hosted ||
|
|
String(deploymentType).toLowerCase() === 'hosted'
|
|
);
|
|
} catch (err) {
|
|
logger.debug(
|
|
{ err },
|
|
'Azure: could not determine deployment type, assuming on-premises',
|
|
);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
export function setEndpoint(e: string): void {
|
|
endpoint = e;
|
|
}
|