Skip to content

Commit 00204dd

Browse files
committed
feat: support & require RH OIDC auth
1 parent 2713313 commit 00204dd

26 files changed

+963
-500
lines changed

.eslintignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
dist

package-lock.json

Lines changed: 32 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,11 @@
7676
"command": "rhda.stackLogs",
7777
"title": "Debug logs",
7878
"category": "Red Hat Dependency Analytics"
79+
},
80+
{
81+
"command": "rhda.authenticate",
82+
"title": "Authenticate",
83+
"category": "Red Hat Dependency Analytics"
7984
}
8085
],
8186
"menus": {
@@ -374,6 +379,21 @@
374379
"type": "string"
375380
},
376381
"description": "List of path globs for manifests to ignore for analysis. Only forward slash is support as a path separator."
382+
},
383+
"redHatDependencyAnalytics.oidc.endpoint": {
384+
"type": "string",
385+
"default": "https://sso.redhat.com/auth/realms/redhat-external",
386+
"description": "URL used for OIDC auth server metadata discovery."
387+
},
388+
"redHatDependencyAnalytics.oidc.clientId": {
389+
"type": "string",
390+
"default": "rhda-vscode",
391+
"description": "Specifies the OIDC client ID."
392+
},
393+
"redHatDependencyAnalytics.oidc.allowInsecure": {
394+
"type": "boolean",
395+
"default": false,
396+
"description": "Enables specifying HTTP-only endpoints."
377397
}
378398
}
379399
}
@@ -436,6 +456,7 @@
436456
"json-to-ast": "^2.1.0",
437457
"minimatch": "^10.0.3",
438458
"mustache": "^4.2.0",
459+
"openid-client": "^6.8.0",
439460
"path": "^0.12.7",
440461
"tree-sitter-python": "^0.23.6",
441462
"web-tree-sitter": "^0.25.6"

src/caStatusBarProvider.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,49 @@ class CAStatusBarProvider implements Disposable {
4747
this.statusBarItem.tooltip = PromptText.LSP_FAILURE_TEXT;
4848
}
4949

50+
/**
51+
* Shows authentication required status in the status bar.
52+
*/
53+
public showAuthRequired(): void {
54+
this.statusBarItem.text = `$(account) RHDA: Not Signed In`;
55+
this.statusBarItem.command = {
56+
title: 'Authenticate with RHDA',
57+
command: 'rhda.authenticate',
58+
};
59+
this.statusBarItem.tooltip = 'Click to sign in for enhanced RHDA features (optional)';
60+
this.statusBarItem.show();
61+
}
62+
63+
/**
64+
* Shows authenticated status in the status bar.
65+
*/
66+
public showAuthenticated(): void {
67+
this.statusBarItem.text = `$(verified) RHDA: Authenticated`;
68+
this.statusBarItem.command = undefined; // No command needed when authenticated
69+
this.statusBarItem.tooltip = 'RHDA is authenticated and ready for dependency analysis';
70+
this.statusBarItem.show();
71+
}
72+
73+
/**
74+
* Shows session expired status in the status bar.
75+
*/
76+
public showSessionExpired(): void {
77+
this.statusBarItem.text = `$(warning) RHDA: Session Expired`;
78+
this.statusBarItem.command = {
79+
title: 'Re-authenticate with RHDA',
80+
command: 'rhda.authenticate',
81+
};
82+
this.statusBarItem.tooltip = 'Your RHDA session has expired. Click to re-authenticate and restore functionality.';
83+
this.statusBarItem.show();
84+
}
85+
86+
/**
87+
* Hides the status bar item.
88+
*/
89+
public hide(): void {
90+
this.statusBarItem.hide();
91+
}
92+
5093
/**
5194
* Disposes of the status bar item.
5295
*/

src/config.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ class Config {
2323
enablePythonBestEffortsInstallation!: string;
2424
usePipDepTree!: string;
2525
vulnerabilityAlertSeverity!: string;
26+
oidcRealmUrl!: string;
27+
oidcClientId!: string;
28+
oidcAllowInsecure!: boolean;
2629
exhortMvnPath!: string;
2730
exhortPreferMvnw!: string;
2831
exhortMvnArgs!: string;
@@ -132,6 +135,9 @@ class Config {
132135
this.exhortPodmanPath = rhdaConfig.podman.executable.path || this.DEFAULT_PODMAN_EXECUTABLE;
133136
this.exhortImagePlatform = rhdaConfig.imagePlatform;
134137
this.excludePatterns = (rhdaConfig.exclude as string[]).map(pattern => new Minimatch(pattern));
138+
this.oidcRealmUrl = rhdaConfig.oidc.endpoint;
139+
this.oidcClientId = rhdaConfig.oidc.clientId;
140+
this.oidcAllowInsecure = rhdaConfig.oidc.allowInsecure;
135141
}
136142

137143
private getEffectiveHttpProxyUrl(): string {

src/dependencyAnalysis/analysis.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
* ------------------------------------------------------------------------------------------ */
55
'use strict';
66

7-
import exhort from '@trustification/exhort-javascript-api';
7+
8+
import exhort, { Options } from '@trustification/exhort-javascript-api';
89
import { AnalysisReport } from '@trustification/exhort-api-spec/model/v4/AnalysisReport';
910

1011
import { globalConfig } from '../config';
@@ -15,6 +16,7 @@ import { notifications, outputChannelDep } from '../extension';
1516
import { Source } from '@trustification/exhort-api-spec/model/v4/Source';
1617
import { DependencyReport } from '@trustification/exhort-api-spec/model/v4/DependencyReport';
1718
import { Issue } from '@trustification/exhort-api-spec/model/v4/Issue';
19+
import { TokenProvider } from '../tokenProvider';
1820

1921
/**
2022
* Represents a source object with an ID and dependencies array.
@@ -146,11 +148,12 @@ class AnalysisResponse implements IAnalysisResponse {
146148
* @param provider - The dependency provider of the corresponding ecosystem.
147149
* @returns A Promise resolving to an AnalysisResponse object.
148150
*/
149-
async function executeComponentAnalysis(diagnosticFilePath: Uri, provider: IDependencyProvider): Promise<AnalysisResponse> {
151+
async function executeComponentAnalysis(tokenProvider: TokenProvider, diagnosticFilePath: Uri, provider: IDependencyProvider): Promise<AnalysisResponse> {
150152

151153
// Define configuration options for the component analysis request
152-
const options = {
153-
'RHDA_TOKEN': globalConfig.telemetryId,
154+
const options: Options = {
155+
'RHDA_TOKEN': await tokenProvider.getToken() ?? '',
156+
'RHDA_TELEMETRY_ID': globalConfig.telemetryId,
154157
'RHDA_SOURCE': globalConfig.utmSource,
155158
'MATCH_MANIFEST_VERSIONS': globalConfig.matchManifestVersions,
156159
'EXHORT_PROXY_URL': globalConfig.exhortProxyUrl,

src/dependencyAnalysis/diagnostics.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { AbstractDiagnosticsPipeline } from '../diagnosticsPipeline';
1515
import { Diagnostic, DiagnosticSeverity, Uri } from 'vscode';
1616
import { notifications, outputChannelDep } from '../extension';
1717
import { globalConfig } from '../config';
18+
import { TokenProvider } from '../tokenProvider';
1819

1920
/**
2021
* Implementation of DiagnosticsPipeline interface.
@@ -95,7 +96,7 @@ class DiagnosticsPipeline extends AbstractDiagnosticsPipeline<DependencyData> {
9596
* @param provider - The dependency provider of the corresponding ecosystem.
9697
* @returns A Promise that resolves when diagnostics are completed.
9798
*/
98-
async function performDiagnostics(diagnosticFilePath: Uri, contents: string, provider: IDependencyProvider) {
99+
async function performDiagnostics(tokenProvider: TokenProvider, diagnosticFilePath: Uri, contents: string, provider: IDependencyProvider) {
99100
try {
100101
const dependencies = provider.collect(contents);
101102
const ecosystem = provider.getEcosystem();
@@ -104,7 +105,7 @@ async function performDiagnostics(diagnosticFilePath: Uri, contents: string, pro
104105
const diagnosticsPipeline = new DiagnosticsPipeline(dependencyMap, diagnosticFilePath);
105106
diagnosticsPipeline.clearDiagnostics();
106107

107-
const response = await executeComponentAnalysis(diagnosticFilePath, provider);
108+
const response = await executeComponentAnalysis(tokenProvider, diagnosticFilePath, provider);
108109

109110
clearCodeActionsMap(diagnosticFilePath);
110111

src/exhortServices.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ function parseImageReference(image: IImageRef, options: IOptions): ImageRef {
5050
* @param source The source for which the token is being validated.
5151
* @returns A promise resolving after validating the token.
5252
*/
53-
async function tokenValidationService(options: { [key: string]: string }, source: string): Promise<string | undefined> {
53+
async function tokenValidationService(options: Options, source: string): Promise<string | undefined> {
5454
try {
5555
// Get token validation status code
5656
const response = await exhort.validateToken(options);

0 commit comments

Comments
 (0)