/******************************************************************************** * Copyright (c) 2019 TypeFox and others * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0. * * SPDX-License-Identifier: EPL-2.0 ********************************************************************************/ import { Extension, UserData, ExtensionCategory, ExtensionReviewList, PersonalAccessToken, SearchResult, NewReview, SuccessResult, ErrorResult, CsrfTokenJson, isError, Namespace, NamespaceDetails, MembershipRole, SortBy, SortOrder, UrlString, NamespaceMembershipList, PublisherInfo, SearchEntry, RegistryVersion, LoginProviders, ScanResultJson, ScanCounts, ScanResultsResponse, ScanFilterOptions, FilesResponse, FileDecisionCountsJson, ScanDecisionRequest, ScanDecisionResponse, FileDecisionRequest, FileDecisionResponse, FileDecisionDeleteRequest, FileDecisionDeleteResponse, Tier, TierList, Customer, CustomerList, UsageStatsList, LogPageableList, CustomerMembershipList, RateLimitToken, Settings, } from './extension-registry-types'; import { createAbsoluteURL, addQuery } from './utils'; import { sendRequest, ErrorResponse } from './server-request'; export class ExtensionRegistryService { readonly admin: AdminService; constructor(readonly serverUrl: string = '', AdminConstructor: AdminServiceConstructor = AdminServiceImpl) { this.admin = new AdminConstructor(this); } async getLoginProviders(abortController: AbortController): Promise> { const endpoint = createAbsoluteURL([this.serverUrl, 'login-providers']); return sendRequest({ abortController, endpoint }); } getLogoutUrl(): string { return createAbsoluteURL([this.serverUrl, 'logout']); } getExtensionApiUrl(ext: { namespace: string, name: string, target?: string, version?: string }): string { const arr = [this.serverUrl, 'api', ext.namespace, ext.name]; if (ext.target) { arr.push(ext.target); } if (ext.version) { arr.push(ext.version); } return createAbsoluteURL(arr); } async getNamespaceDetails(abortController: AbortController, name: string): Promise> { const endpoint = createAbsoluteURL([this.serverUrl, 'api', name, 'details']); return sendRequest({ abortController, endpoint }); } async setNamespaceDetails(abortController: AbortController, endpoint: string, details: NamespaceDetails): Promise> { const csrfResponse = await this.getCsrfToken(abortController); const headers: Record = { 'Content-Type': 'application/json;charset=UTF-8' }; if (!isError(csrfResponse)) { const csrfToken = csrfResponse as CsrfTokenJson; headers[csrfToken.header] = csrfToken.value; } return sendRequest({ abortController, method: 'POST', payload: details, credentials: true, endpoint, headers }); } async setNamespaceLogo(abortController: AbortController, endpoint: string, logoFile: Blob, logoName: string): Promise> { const csrfResponse = await this.getCsrfToken(abortController); const headers: Record = {}; if (!isError(csrfResponse)) { const csrfToken = csrfResponse as CsrfTokenJson; headers[csrfToken.header] = csrfToken.value; } const form = new FormData(); form.append('file', logoFile, logoName); endpoint = createAbsoluteURL([endpoint, 'logo']); return sendRequest({ abortController, method: 'POST', payload: form, credentials: true, endpoint, headers }); } async search(abortController: AbortController, filter?: ExtensionFilter): Promise> { const query: { key: string, value: string | number }[] = []; if (filter) { if (filter.query) query.push({ key: 'query', value: filter.query }); if (filter.category) query.push({ key: 'category', value: filter.category }); if (filter.offset) query.push({ key: 'offset', value: filter.offset }); if (filter.size) query.push({ key: 'size', value: filter.size }); if (filter.sortBy) query.push({ key: 'sortBy', value: filter.sortBy }); if (filter.sortOrder) query.push({ key: 'sortOrder', value: filter.sortOrder }); } const endpoint = createAbsoluteURL([this.serverUrl, 'api', '-', 'search'], query); return sendRequest({ abortController, endpoint }); } async getExtensionDetail(abortController: AbortController, extensionUrl: UrlString): Promise> { return sendRequest({ abortController, endpoint: extensionUrl }); } async getExtensionReadme(abortController: AbortController, extension: Extension): Promise { return sendRequest({ abortController, endpoint: extension.files.readme, headers: { 'Accept': 'text/plain' }, followRedirect: true }); } async getExtensionChangelog(abortController: AbortController, extension: Extension): Promise { return sendRequest({ abortController, endpoint: extension.files.changelog, headers: { 'Accept': 'text/plain' }, followRedirect: true }); } async getExtensionIcon(abortController: AbortController, extension: Extension | SearchEntry): Promise { if (!extension.files.icon) { return Promise.resolve(undefined); } return sendRequest({ abortController, endpoint: extension.files.icon, headers: { 'Accept': 'application/octet-stream' }, followRedirect: true }).then(value => { const blob = value as Blob; return URL.createObjectURL(blob); }); } getCategories(): ExtensionCategory[] { return [ 'Programming Languages', 'Snippets', 'Linters', 'Themes', 'Debuggers', 'Formatters', 'Keymaps', 'SCM Providers', 'Other', 'Extension Packs', 'Language Packs', 'Data Science', 'Machine Learning', 'Visualization', 'Notebooks' ]; } async getExtensionReviews(abortController: AbortController, extension: Extension): Promise> { return sendRequest({ abortController, endpoint: extension.reviewsUrl }); } async postReview(abortController: AbortController, review: NewReview, postReviewUrl: UrlString): Promise> { const csrfResponse = await this.getCsrfToken(abortController); const headers: Record = { 'Content-Type': 'application/json;charset=UTF-8' }; if (!isError(csrfResponse)) { const csrfToken = csrfResponse as CsrfTokenJson; headers[csrfToken.header] = csrfToken.value; } return sendRequest({ abortController, method: 'POST', payload: review, credentials: true, endpoint: postReviewUrl, headers }); } async deleteReview(abortController: AbortController, deleteReviewUrl: string): Promise> { const csrfResponse = await this.getCsrfToken(abortController); const headers: Record = {}; if (!isError(csrfResponse)) { const csrfToken = csrfResponse as CsrfTokenJson; headers[csrfToken.header] = csrfToken.value; } return sendRequest({ abortController, method: 'POST', credentials: true, endpoint: deleteReviewUrl, headers }); } async deleteUserReview(abortController: AbortController, extension: Extension, user: UserData): Promise> { const csrfResponse = await this.getCsrfToken(abortController); const headers: Record = {}; if (!isError(csrfResponse)) { const csrfToken = csrfResponse as CsrfTokenJson; headers[csrfToken.header] = csrfToken.value; } return sendRequest({ abortController, method: 'POST', credentials: true, endpoint: createAbsoluteURL([this.serverUrl, 'admin', 'extension', extension.namespace, extension.name, 'review', user.provider || 'github', user.loginName, 'delete']), headers }); } async getUser(abortController: AbortController): Promise> { return sendRequest({ abortController, endpoint: createAbsoluteURL([this.serverUrl, 'user']), credentials: true }); } async getUserAuthError(abortController: AbortController): Promise> { return sendRequest({ abortController, endpoint: createAbsoluteURL([this.serverUrl, 'user', 'auth-error']), credentials: true }); } async getUserByName(abortController: AbortController, name: string): Promise[]> { return sendRequest({ abortController, endpoint: createAbsoluteURL([this.serverUrl, 'user', 'search', name]), credentials: true }); } async getAccessTokens(abortController: AbortController, user: UserData): Promise[]> { return sendRequest({ abortController, credentials: true, endpoint: user.tokensUrl }); } async createAccessToken(abortController: AbortController, user: UserData, description: string): Promise> { const csrfResponse = await this.getCsrfToken(abortController); const headers: Record = {}; if (!isError(csrfResponse)) { const csrfToken = csrfResponse as CsrfTokenJson; headers[csrfToken.header] = csrfToken.value; } const endpoint = addQuery(user.createTokenUrl, [{ key: 'description', value: description }]); return sendRequest({ abortController, method: 'POST', credentials: true, endpoint, headers }); } async deleteAccessToken(abortController: AbortController, token: PersonalAccessToken): Promise> { const csrfResponse = await this.getCsrfToken(abortController); const headers: Record = {}; if (!isError(csrfResponse)) { const csrfToken = csrfResponse as CsrfTokenJson; headers[csrfToken.header] = csrfToken.value; } return sendRequest({ abortController, method: 'POST', credentials: true, endpoint: token.deleteTokenUrl, headers }); } async deleteAllAccessTokens(abortController: AbortController, tokens: PersonalAccessToken[]): Promise[]> { const csrfResponse = await this.getCsrfToken(abortController); const headers: Record = {}; if (!isError(csrfResponse)) { const csrfToken = csrfResponse as CsrfTokenJson; headers[csrfToken.header] = csrfToken.value; } return await Promise.all(tokens.map(token => sendRequest({ abortController, method: 'POST', credentials: true, endpoint: token.deleteTokenUrl, headers }))); } async getCsrfToken(abortController: AbortController): Promise> { return sendRequest({ abortController, credentials: true, endpoint: createAbsoluteURL([this.serverUrl, 'user', 'csrf']) }); } async getNamespaces(abortController: AbortController): Promise[]> { return sendRequest({ abortController, credentials: true, endpoint: createAbsoluteURL([this.serverUrl, 'user', 'namespaces']) }); } async getCustomers(abortController: AbortController): Promise[]> { return sendRequest({ abortController, credentials: true, endpoint: createAbsoluteURL([this.serverUrl, 'user', 'customers']) }); } async getUsageStats(abortController: AbortController, customerName: string, date: Date): Promise> { const query: { key: string, value: string | number }[] = []; query.push({ key: 'date', value: date.toISOString() }); return sendRequest({ abortController, endpoint: createAbsoluteURL([this.serverUrl, 'user', 'customers', customerName, 'usage'], query), credentials: true }, false); } async getNamespaceMembers(abortController: AbortController, namespace: Namespace): Promise> { return sendRequest({ abortController, credentials: true, endpoint: namespace.membersUrl }); } async setNamespaceMember(abortController: AbortController, endpoint: UrlString, user: UserData, role: MembershipRole | 'remove'): Promise[]> { const csrfResponse = await this.getCsrfToken(abortController); const headers: Record = {}; if (!isError(csrfResponse)) { const csrfToken = csrfResponse as CsrfTokenJson; headers[csrfToken.header] = csrfToken.value; } const query = [ { key: 'user', value: user.loginName }, { key: 'provider', value: user.provider }, { key: 'role', value: role } ]; return sendRequest({ abortController, headers, method: 'POST', credentials: true, endpoint: addQuery(endpoint, query) }); } async signPublisherAgreement(abortController: AbortController): Promise> { const csrfResponse = await this.getCsrfToken(abortController); const headers: Record = {}; if (!isError(csrfResponse)) { const csrfToken = csrfResponse as CsrfTokenJson; headers[csrfToken.header] = csrfToken.value; } return sendRequest({ abortController, method: 'POST', credentials: true, endpoint: createAbsoluteURL([this.serverUrl, 'user', 'publisher-agreement']), headers }); } async getStaticContent(abortController: AbortController, url: string): Promise { return sendRequest({ abortController, endpoint: url, headers: { 'Accept': 'text/plain' }, followRedirect: true }); } async publishExtension(abortController: AbortController, extensionPackage: File): Promise> { const csrfResponse = await this.getCsrfToken(abortController); const headers: Record = { 'Content-Type': 'application/octet-stream' }; if (!isError(csrfResponse)) { const csrfToken = csrfResponse as CsrfTokenJson; headers[csrfToken.header] = csrfToken.value; } return sendRequest({ abortController, method: 'POST', credentials: true, payload: extensionPackage, headers: headers, endpoint: createAbsoluteURL([this.serverUrl, 'api', 'user', 'publish']) }, false); // do not retry publishing an extension but show the explicit error received } async createNamespace(abortController: AbortController, name: string): Promise> { const csrfResponse = await this.getCsrfToken(abortController); const headers: Record = { 'Content-Type': 'application/json;charset=UTF-8' }; if (!isError(csrfResponse)) { const csrfToken = csrfResponse as CsrfTokenJson; headers[csrfToken.header] = csrfToken.value; } return sendRequest({ abortController, method: 'POST', credentials: true, payload: { name: name }, headers: headers, endpoint: createAbsoluteURL([this.serverUrl, 'api', 'user', 'namespace', 'create']) }); } async getExtensions(abortController: AbortController): Promise> { const csrfResponse = await this.getCsrfToken(abortController); const headers: Record = {}; if (!isError(csrfResponse)) { const csrfToken = csrfResponse as CsrfTokenJson; headers[csrfToken.header] = csrfToken.value; } return sendRequest({ abortController, method: 'GET', credentials: true, headers: headers, endpoint: createAbsoluteURL([this.serverUrl, 'user', 'extensions']) }); } async getExtension(abortController: AbortController, namespace: string, extension: string): Promise> { const csrfResponse = await this.getCsrfToken(abortController); const headers: Record = {}; if (!isError(csrfResponse)) { const csrfToken = csrfResponse as CsrfTokenJson; headers[csrfToken.header] = csrfToken.value; } return sendRequest({ abortController, method: 'GET', credentials: true, headers: headers, endpoint: createAbsoluteURL([this.serverUrl, 'user', 'extension', namespace, extension]) }); } async deleteExtensions(abortController: AbortController, req: { namespace: string, extension: string, targetPlatformVersions?: object[] }): Promise> { const csrfResponse = await this.getCsrfToken(abortController); const headers: Record = { 'Content-Type': 'application/json;charset=UTF-8' }; if (!isError(csrfResponse)) { const csrfToken = csrfResponse as CsrfTokenJson; headers[csrfToken.header] = csrfToken.value; } return sendRequest({ abortController, method: 'POST', credentials: true, endpoint: createAbsoluteURL([this.serverUrl, 'user', 'extension', req.namespace, req.extension, 'delete']), headers, payload: req.targetPlatformVersions }); } async getRegistryVersion(abortController: AbortController): Promise> { const endpoint = createAbsoluteURL([this.serverUrl, 'api', 'version']); return sendRequest({ abortController, endpoint }); } } export interface AdminService { getExtension(abortController: AbortController, namespace: string, extension: string): Promise> deleteExtensions(abortController: AbortController, req: { namespace: string, extension: string, targetPlatformVersions?: object[] }): Promise> getNamespace(abortController: AbortController, name: string): Promise> createNamespace(abortController: AbortController, namespace: { name: string }): Promise> deleteNamespace(abortController: AbortController, namespace: { name: string }): Promise> changeNamespace(abortController: AbortController, req: {oldNamespace: string, newNamespace: string, removeOldNamespace: boolean, mergeIfNewNamespaceAlreadyExists: boolean}): Promise> getPublisherInfo(abortController: AbortController, provider: string, login: string): Promise> revokePublisherContributions(abortController: AbortController, provider: string, login: string): Promise> revokeAccessTokens(abortController: AbortController, provider: string, login: string): Promise> getAllScans(abortController: AbortController, params?: { size?: number; offset?: number; status?: string | string[]; publisher?: string; namespace?: string; name?: string; validationType?: string[]; threatScannerName?: string[]; dateStartedFrom?: string; dateStartedTo?: string; enforcement?: 'enforced' | 'notEnforced' | 'all' }): Promise> getScan(abortController: AbortController, scanId: string): Promise> retryFailedScannerJobs(abortController: AbortController, scanId: string): Promise> getScanCounts(abortController: AbortController, params?: { dateStartedFrom?: string; dateStartedTo?: string; enforcement?: 'enforced' | 'notEnforced' | 'all'; threatScannerName?: string[]; validationType?: string[] }): Promise> getScanFilterOptions(abortController: AbortController): Promise> // Files API getFiles(abortController: AbortController, params?: { size?: number; offset?: number; decision?: string; publisher?: string; namespace?: string; name?: string; dateDecidedFrom?: string; dateDecidedTo?: string; sortBy?: string; sortOrder?: 'asc' | 'desc' }): Promise>; getFileCounts(abortController: AbortController, params?: { dateDecidedFrom?: string; dateDecidedTo?: string }): Promise> // Decision APIs makeScanDecision(abortController: AbortController, request: ScanDecisionRequest): Promise> makeFileDecision(abortController: AbortController, request: FileDecisionRequest): Promise> deleteFileDecisions(abortController: AbortController, request: FileDecisionDeleteRequest): Promise> getTiers(abortController: AbortController): Promise>; createTier(abortController: AbortController, tier: Tier): Promise>; updateTier(abortController: AbortController, name: string, tier: Tier): Promise>; deleteTier(abortController: AbortController, name: string): Promise>; getCustomers(abortController: AbortController): Promise>; getCustomer(abortController: AbortController, name: string): Promise>; createCustomer(abortController: AbortController, customer: Customer): Promise>; updateCustomer(abortController: AbortController, name: string, customer: Customer): Promise>; deleteCustomer(abortController: AbortController, name: string): Promise>; getCustomerMembers(abortController: AbortController, name: string): Promise>; addCustomerMember(abortController: AbortController, name: string, user: UserData): Promise>; removeCustomerMember(abortController: AbortController, name: string, user: UserData): Promise>; getUsageStats(abortController: AbortController, customerName: string, date: Date): Promise>; getLogs(abortController: AbortController, page?: number, size?: number, period?: string): Promise>; getCustomerRateLimitTokens(abortController: AbortController, customerName: string): Promise>; createCustomerRateLimitToken(abortController: AbortController, customerName: string, description: string): Promise>; deleteCustomerRateLimitToken(abortController: AbortController, customerName: string, tokenId: number): Promise>; getSettings(abortController: AbortController): Promise>; updateSettings(abortController: AbortController, settings: Settings): Promise>; } export interface AdminServiceConstructor { new (registry: ExtensionRegistryService): AdminService } export class AdminServiceImpl implements AdminService { constructor(readonly registry: ExtensionRegistryService) {} async getExtension(abortController: AbortController, namespace: string, extension: string): Promise> { return sendRequest({ abortController, credentials: true, endpoint: createAbsoluteURL([this.registry.serverUrl, 'admin', 'extension', namespace, extension]) }); } async deleteExtensions(abortController: AbortController, req: { namespace: string, extension: string, targetPlatformVersions?: object[] }): Promise> { const csrfResponse = await this.registry.getCsrfToken(abortController); const headers: Record = { 'Content-Type': 'application/json;charset=UTF-8' }; if (!isError(csrfResponse)) { const csrfToken = csrfResponse as CsrfTokenJson; headers[csrfToken.header] = csrfToken.value; } return sendRequest({ abortController, method: 'POST', credentials: true, endpoint: createAbsoluteURL([this.registry.serverUrl, 'admin', 'extension', req.namespace, req.extension, 'delete']), headers, payload: req.targetPlatformVersions }); } async getNamespace(abortController: AbortController, name: string): Promise> { return sendRequest({ abortController, credentials: true, endpoint: createAbsoluteURL([this.registry.serverUrl, 'admin', 'namespace', name]) }); } async createNamespace(abortController: AbortController, namespace: { name: string }): Promise> { const csrfResponse = await this.registry.getCsrfToken(abortController); const headers: Record = { 'Content-Type': 'application/json;charset=UTF-8' }; if (!isError(csrfResponse)) { const csrfToken = csrfResponse as CsrfTokenJson; headers[csrfToken.header] = csrfToken.value; } return sendRequest({ abortController, credentials: true, endpoint: createAbsoluteURL([this.registry.serverUrl, 'admin', 'create-namespace']), method: 'POST', payload: namespace, headers }); } async deleteNamespace(abortController: AbortController, namespace: { name: string }): Promise> { const csrfResponse = await this.registry.getCsrfToken(abortController); const headers: Record = { 'Content-Type': 'application/json;charset=UTF-8' }; if (!isError(csrfResponse)) { const csrfToken = csrfResponse as CsrfTokenJson; headers[csrfToken.header] = csrfToken.value; } return sendRequest({ abortController, credentials: true, endpoint: createAbsoluteURL([this.registry.serverUrl, 'admin', 'namespace', namespace.name]), method: 'DELETE', headers }); } async changeNamespace(abortController: AbortController, req: {oldNamespace: string, newNamespace: string, removeOldNamespace: boolean, mergeIfNewNamespaceAlreadyExists: boolean}): Promise> { const csrfResponse = await this.registry.getCsrfToken(abortController); const headers: Record = { 'Content-Type': 'application/json;charset=UTF-8' }; if (!isError(csrfResponse)) { const csrfToken = csrfResponse as CsrfTokenJson; headers[csrfToken.header] = csrfToken.value; } return sendRequest({ abortController, credentials: true, endpoint: createAbsoluteURL([this.registry.serverUrl, 'admin', 'change-namespace']), method: 'POST', payload: req, headers }); } async getPublisherInfo(abortController: AbortController, provider: string, login: string): Promise> { return sendRequest({ abortController, endpoint: createAbsoluteURL([this.registry.serverUrl, 'admin', 'publisher', provider, login]), credentials: true }); } async revokePublisherContributions(abortController: AbortController, provider: string, login: string): Promise> { const csrfResponse = await this.registry.getCsrfToken(abortController); const headers: Record = {}; if (!isError(csrfResponse)) { const csrfToken = csrfResponse as CsrfTokenJson; headers[csrfToken.header] = csrfToken.value; } return sendRequest({ abortController, method: 'POST', credentials: true, endpoint: createAbsoluteURL([this.registry.serverUrl, 'admin', 'publisher', provider, login, 'revoke']), headers }); } async revokeAccessTokens(abortController: AbortController, provider: string, login: string): Promise> { const csrfResponse = await this.registry.getCsrfToken(abortController); const headers: Record = {}; if (!isError(csrfResponse)) { const csrfToken = csrfResponse as CsrfTokenJson; headers[csrfToken.header] = csrfToken.value; } return sendRequest({ abortController, method: 'POST', credentials: true, endpoint: createAbsoluteURL([this.registry.serverUrl, 'admin', 'publisher', provider, login, 'tokens', 'revoke']), headers }); } async getAllScans(abortController: AbortController, params?: { size?: number; offset?: number; status?: string | string[]; publisher?: string; namespace?: string; name?: string; validationType?: string[]; threatScannerName?: string[]; dateStartedFrom?: string; dateStartedTo?: string; enforcement?: 'enforced' | 'notEnforced' | 'all'; adminDecision?: string[] }): Promise> { const query: { key: string, value: string | number }[] = []; if (params) { if (params.size !== undefined) query.push({ key: 'size', value: params.size }); if (params.offset !== undefined) query.push({ key: 'offset', value: params.offset }); if (params.status) { const statusValue = Array.isArray(params.status) ? params.status.join(',') : params.status; query.push({ key: 'status', value: statusValue }); } if (params.publisher) query.push({ key: 'publisher', value: params.publisher }); if (params.namespace) query.push({ key: 'namespace', value: params.namespace }); if (params.name) query.push({ key: 'name', value: params.name }); if (params.validationType && params.validationType.length > 0) query.push({ key: 'validationType', value: params.validationType.join(',') }); if (params.threatScannerName && params.threatScannerName.length > 0) query.push({ key: 'threatScannerName', value: params.threatScannerName.join(',') }); if (params.dateStartedFrom) query.push({ key: 'dateStartedFrom', value: params.dateStartedFrom }); if (params.dateStartedTo) query.push({ key: 'dateStartedTo', value: params.dateStartedTo }); if (params.enforcement) query.push({ key: 'enforcement', value: params.enforcement }); if (params.adminDecision && params.adminDecision.length > 0) query.push({ key: 'adminDecision', value: params.adminDecision.join(',') }); } const endpoint = createAbsoluteURL([this.registry.serverUrl, 'admin', 'scans'], query); return sendRequest({ abortController, credentials: true, endpoint }); } async getScan(abortController: AbortController, scanId: string): Promise> { return sendRequest({ abortController, credentials: true, endpoint: createAbsoluteURL([this.registry.serverUrl, 'admin', 'scans', scanId]) }); } async retryFailedScannerJobs(abortController: AbortController, scanId: string): Promise> { const csrfResponse = await this.registry.getCsrfToken(abortController); const headers: Record = {}; if (!isError(csrfResponse)) { const csrfToken = csrfResponse as CsrfTokenJson; headers[csrfToken.header] = csrfToken.value; } return sendRequest({ abortController, method: 'POST', credentials: true, endpoint: createAbsoluteURL([this.registry.serverUrl, 'admin', 'scans', scanId, 'jobs', 'retry']), headers, }); } async getScanCounts(abortController: AbortController, params?: { dateStartedFrom?: string; dateStartedTo?: string; enforcement?: 'enforced' | 'notEnforced' | 'all'; threatScannerName?: string[]; validationType?: string[] }): Promise> { const query: { key: string, value: string | number }[] = []; if (params) { if (params.dateStartedFrom) query.push({ key: 'dateStartedFrom', value: params.dateStartedFrom }); if (params.dateStartedTo) query.push({ key: 'dateStartedTo', value: params.dateStartedTo }); if (params.enforcement) query.push({ key: 'enforcement', value: params.enforcement }); if (params.threatScannerName && params.threatScannerName.length > 0) { query.push({ key: 'threatScannerName', value: params.threatScannerName.join(',') }); } if (params.validationType && params.validationType.length > 0) { query.push({ key: 'validationType', value: params.validationType.join(',') }); } } const endpoint = createAbsoluteURL([this.registry.serverUrl, 'admin', 'scans', 'counts'], query); return sendRequest({ abortController, credentials: true, endpoint }); } async getScanFilterOptions(abortController: AbortController): Promise> { return sendRequest({ abortController, credentials: true, endpoint: createAbsoluteURL([this.registry.serverUrl, 'admin', 'scans', 'filterOptions']) }); } async getFiles(abortController: AbortController, params?: { size?: number; offset?: number; decision?: string; publisher?: string; namespace?: string; name?: string; dateDecidedFrom?: string; dateDecidedTo?: string; sortBy?: string; sortOrder?: 'asc' | 'desc' }): Promise> { const query: { key: string, value: string | number }[] = []; if (params) { if (params.size !== undefined) query.push({ key: 'size', value: params.size }); if (params.offset !== undefined) query.push({ key: 'offset', value: params.offset }); if (params.decision) query.push({ key: 'decision', value: params.decision }); if (params.publisher) query.push({ key: 'publisher', value: params.publisher }); if (params.namespace) query.push({ key: 'namespace', value: params.namespace }); if (params.name) query.push({ key: 'name', value: params.name }); if (params.dateDecidedFrom) query.push({ key: 'dateDecidedFrom', value: params.dateDecidedFrom }); if (params.dateDecidedTo) query.push({ key: 'dateDecidedTo', value: params.dateDecidedTo }); if (params.sortBy) query.push({ key: 'sortBy', value: params.sortBy }); if (params.sortOrder) query.push({ key: 'sortOrder', value: params.sortOrder }); } const endpoint = createAbsoluteURL([this.registry.serverUrl, 'admin', 'scans', 'files'], query); return sendRequest({ abortController, credentials: true, endpoint }); } async getFileCounts(abortController: AbortController, params?: { dateDecidedFrom?: string; dateDecidedTo?: string }): Promise> { const query: { key: string, value: string | number }[] = []; if (params) { if (params.dateDecidedFrom) query.push({ key: 'dateDecidedFrom', value: params.dateDecidedFrom }); if (params.dateDecidedTo) query.push({ key: 'dateDecidedTo', value: params.dateDecidedTo }); } const endpoint = createAbsoluteURL([this.registry.serverUrl, 'admin', 'scans', 'files', 'counts'], query); return sendRequest({ abortController, credentials: true, endpoint }); } async makeScanDecision(abortController: AbortController, request: ScanDecisionRequest): Promise> { const csrfResponse = await this.registry.getCsrfToken(abortController); const headers: Record = { 'Content-Type': 'application/json;charset=UTF-8' }; if (!isError(csrfResponse)) { const csrfToken = csrfResponse as CsrfTokenJson; headers[csrfToken.header] = csrfToken.value; } return sendRequest({ abortController, method: 'POST', credentials: true, endpoint: createAbsoluteURL([this.registry.serverUrl, 'admin', 'scans', 'decisions']), headers, payload: request }); } async makeFileDecision(abortController: AbortController, request: FileDecisionRequest): Promise> { const csrfResponse = await this.registry.getCsrfToken(abortController); const headers: Record = { 'Content-Type': 'application/json;charset=UTF-8' }; if (!isError(csrfResponse)) { const csrfToken = csrfResponse as CsrfTokenJson; headers[csrfToken.header] = csrfToken.value; } return sendRequest({ abortController, method: 'POST', credentials: true, endpoint: createAbsoluteURL([this.registry.serverUrl, 'admin', 'scans', 'files', 'decisions']), headers, payload: request }); } async deleteFileDecisions(abortController: AbortController, request: FileDecisionDeleteRequest): Promise> { const csrfResponse = await this.registry.getCsrfToken(abortController); const headers: Record = { 'Content-Type': 'application/json;charset=UTF-8' }; if (!isError(csrfResponse)) { const csrfToken = csrfResponse as CsrfTokenJson; headers[csrfToken.header] = csrfToken.value; } return sendRequest({ abortController, method: 'DELETE', credentials: true, endpoint: createAbsoluteURL([this.registry.serverUrl, 'admin', 'scans', 'files', 'decisions']), headers, payload: request }); } async getTiers(abortController: AbortController): Promise> { return sendRequest({ abortController, endpoint: createAbsoluteURL([this.registry.serverUrl, 'admin', 'ratelimit', 'tiers']), credentials: true }, false); } async createTier(abortController: AbortController, tier: Tier): Promise> { const csrfResponse = await this.registry.getCsrfToken(abortController); const headers: Record = { 'Content-Type': 'application/json;charset=UTF-8' }; if (!isError(csrfResponse)) { const csrfToken = csrfResponse as CsrfTokenJson; headers[csrfToken.header] = csrfToken.value; } return sendRequest({ abortController, method: 'POST', payload: tier, credentials: true, endpoint: createAbsoluteURL([this.registry.serverUrl, 'admin', 'ratelimit', 'tiers', 'create']), headers }, false); } async updateTier(abortController: AbortController, name: string, tier: Tier): Promise> { const csrfResponse = await this.registry.getCsrfToken(abortController); const headers: Record = { 'Content-Type': 'application/json;charset=UTF-8' }; if (!isError(csrfResponse)) { const csrfToken = csrfResponse as CsrfTokenJson; headers[csrfToken.header] = csrfToken.value; } return sendRequest({ abortController, method: 'PUT', payload: tier, credentials: true, endpoint: createAbsoluteURL([this.registry.serverUrl, 'admin', 'ratelimit', 'tiers', name]), headers }, false); } async deleteTier(abortController: AbortController, name: string): Promise { const csrfResponse = await this.registry.getCsrfToken(abortController); const headers: Record = { 'Content-Type': 'application/json;charset=UTF-8' }; if (!isError(csrfResponse)) { const csrfToken = csrfResponse as CsrfTokenJson; headers[csrfToken.header] = csrfToken.value; } return sendRequest({ abortController, method: 'DELETE', credentials: true, endpoint: createAbsoluteURL([this.registry.serverUrl, 'admin', 'ratelimit', 'tiers', name]), headers }, false); } async getCustomers(abortController: AbortController): Promise> { return await sendRequest({ abortController, endpoint: createAbsoluteURL([this.registry.serverUrl, 'admin', 'ratelimit', 'customers']), credentials: true }, false); } async getCustomer(abortController: AbortController, name: string): Promise> { return await sendRequest({ abortController, endpoint: createAbsoluteURL([this.registry.serverUrl, 'admin', 'ratelimit', 'customers', name]), credentials: true }, false); } async createCustomer(abortController: AbortController, customer: Customer): Promise> { const csrfResponse = await this.registry.getCsrfToken(abortController); const headers: Record = { 'Content-Type': 'application/json;charset=UTF-8' }; if (!isError(csrfResponse)) { const csrfToken = csrfResponse as CsrfTokenJson; headers[csrfToken.header] = csrfToken.value; } return sendRequest({ abortController, method: 'POST', payload: customer, credentials: true, endpoint: createAbsoluteURL([this.registry.serverUrl, 'admin', 'ratelimit', 'customers', 'create']), headers }, false); } async updateCustomer(abortController: AbortController, name: string, customer: Customer): Promise> { const csrfResponse = await this.registry.getCsrfToken(abortController); const headers: Record = { 'Content-Type': 'application/json;charset=UTF-8' }; if (!isError(csrfResponse)) { const csrfToken = csrfResponse as CsrfTokenJson; headers[csrfToken.header] = csrfToken.value; } return sendRequest({ abortController, method: 'PUT', payload: customer, credentials: true, endpoint: createAbsoluteURL([this.registry.serverUrl, 'admin', 'ratelimit', 'customers', name]), headers }, false); } async deleteCustomer(abortController: AbortController, name: string): Promise { const csrfResponse = await this.registry.getCsrfToken(abortController); const headers: Record = { 'Content-Type': 'application/json;charset=UTF-8' }; if (!isError(csrfResponse)) { const csrfToken = csrfResponse as CsrfTokenJson; headers[csrfToken.header] = csrfToken.value; } return sendRequest({ abortController, method: 'DELETE', credentials: true, endpoint: createAbsoluteURL([this.registry.serverUrl, 'admin', 'ratelimit', 'customers', name]), headers }, false); } async getCustomerMembers(abortController: AbortController, name: string): Promise> { return sendRequest({ abortController, credentials: true, endpoint: createAbsoluteURL([this.registry.serverUrl, 'admin', 'ratelimit', 'customers', name, "members"]), }, false); } async addCustomerMember(abortController: AbortController, name: string, user: UserData): Promise> { const csrfResponse = await this.registry.getCsrfToken(abortController); const headers: Record = {}; if (!isError(csrfResponse)) { const csrfToken = csrfResponse as CsrfTokenJson; headers[csrfToken.header] = csrfToken.value; } const query = [ { key: 'user', value: user.loginName }, { key: 'provider', value: user.provider }, ]; return sendRequest({ abortController, headers, method: 'POST', credentials: true, endpoint: addQuery(createAbsoluteURL([this.registry.serverUrl, 'admin', 'ratelimit', 'customers', name, "add-member"]), query), }, false); } async removeCustomerMember(abortController: AbortController, name: string, user: UserData): Promise> { const csrfResponse = await this.registry.getCsrfToken(abortController); const headers: Record = {}; if (!isError(csrfResponse)) { const csrfToken = csrfResponse as CsrfTokenJson; headers[csrfToken.header] = csrfToken.value; } const query = [ { key: 'user', value: user.loginName }, { key: 'provider', value: user.provider }, ]; return sendRequest({ abortController, headers, method: 'POST', credentials: true, endpoint: addQuery(createAbsoluteURL([this.registry.serverUrl, 'admin', 'ratelimit', 'customers', name, "remove-member"]), query), }, false); } /** * Get usage stats for a customer within an optional date range. */ async getUsageStats( abortController: AbortController, customerName: string, date: Date, ): Promise> { const query: { key: string, value: string | number }[] = []; query.push({ key: 'date', value: date.toISOString() }); return sendRequest({ abortController, endpoint: createAbsoluteURL([this.registry.serverUrl, 'admin', 'ratelimit', 'customers', customerName, 'usage'], query), credentials: true }, false); } async getLogs( abortController: AbortController, page: number = 0, size: number = 20, period?: string ): Promise> { const query: { key: string, value: string | number }[] = [ { key: 'page', value: page }, { key: 'size', value: size } ]; if (period) { query.push({ key: 'period', value: period }); } const endpoint = addQuery(createAbsoluteURL([this.registry.serverUrl, 'admin', 'logs']), query); return sendRequest({ abortController, endpoint, credentials: true }, false); } async getCustomerRateLimitTokens(abortController: AbortController, customerName: string): Promise> { return sendRequest({ abortController, credentials: true, endpoint: createAbsoluteURL([this.registry.serverUrl, 'admin', 'ratelimit', 'customers', customerName, 'tokens']), }, false); } async createCustomerRateLimitToken(abortController: AbortController, customerName: string, description?: string): Promise> { const csrfResponse = await this.registry.getCsrfToken(abortController); const headers: Record = {}; if (!isError(csrfResponse)) { const csrfToken = csrfResponse as CsrfTokenJson; headers[csrfToken.header] = csrfToken.value; } const url = createAbsoluteURL([this.registry.serverUrl, 'admin', 'ratelimit', 'customers', customerName, 'tokens']); const endpoint = description !== undefined ? addQuery(url, [{ key: 'description', value: description }]) : url; return sendRequest({ abortController, method: 'POST', credentials: true, endpoint, headers }, false); } async deleteCustomerRateLimitToken(abortController: AbortController, customerName: string, tokenId: number): Promise> { const csrfResponse = await this.registry.getCsrfToken(abortController); const headers: Record = { 'Content-Type': 'application/json;charset=UTF-8' }; if (!isError(csrfResponse)) { const csrfToken = csrfResponse as CsrfTokenJson; headers[csrfToken.header] = csrfToken.value; } return sendRequest({ abortController, method: 'DELETE', credentials: true, endpoint: createAbsoluteURL([this.registry.serverUrl, 'admin', 'ratelimit', 'customers', customerName, 'tokens', `${tokenId}`]), headers }, false); } async getSettings(abortController: AbortController): Promise> { return sendRequest({ abortController, credentials: true, endpoint: createAbsoluteURL([this.registry.serverUrl, 'admin', 'settings']), }, false); } async updateSettings(abortController: AbortController, settings: Settings): Promise> { const csrfResponse = await this.registry.getCsrfToken(abortController); const headers: Record = { 'Content-Type': 'application/json;charset=UTF-8' }; if (!isError(csrfResponse)) { const csrfToken = csrfResponse as CsrfTokenJson; headers[csrfToken.header] = csrfToken.value; } return sendRequest({ abortController, method: 'PUT', payload: settings, credentials: true, endpoint: createAbsoluteURL([this.registry.serverUrl, 'admin', 'settings']), headers }, false); } } export interface ExtensionFilter { query: string; category: ExtensionCategory | ''; size: number; offset: number; sortBy: SortBy; sortOrder: SortOrder; }