Skip to content

Enhance tenant management and dialog components #45

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions app/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ interface Tenant {
clientSecret: string | null;
name: string;
authType: string; // Global authentication type
tenantName: string;
}

async function getConfig(): Promise<CLIConfig> {
Expand Down Expand Up @@ -422,14 +423,16 @@ export const getTenants = async () => {

const tenants: Tenant[] = [];
for (let environment of Object.keys(config.environments)) {
const envConfig = config.environments[environment];
tenants.push({
active: environment === activeEnv,
name: environment,
apiUrl: config.environments[environment].baseurl,
tenantUrl: config.environments[environment].tenanturl,
apiUrl: envConfig.baseurl,
tenantUrl: envConfig.tenanturl,
clientId: await getClientId(environment),
clientSecret: await getClientSecret(environment),
authType: config.authtype,
tenantName: environment,
});
}
return tenants;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ export interface DialogData {
showSpinner?: boolean;
showCancel?: boolean;
disableClose?: boolean;
confirmText?: string;
cancelText?: string;
isConfirmation?: boolean;
}

@Component({
Expand All @@ -32,7 +35,18 @@ export interface DialogData {
</p>
</div>
<div mat-dialog-actions align="end">
<button mat-button (click)="onClose()" *ngIf="data.showCancel !== false">
<!-- Confirmation Dialog Buttons -->
<ng-container *ngIf="data.isConfirmation">
<button mat-button (click)="onCancel()">
{{ data.cancelText || 'Cancel' }}
</button>
<button mat-raised-button color="warn" (click)="onConfirm()">
{{ data.confirmText || 'Confirm' }}
</button>
</ng-container>

<!-- Standard Dialog Button -->
<button mat-button (click)="onClose()" *ngIf="!data.isConfirmation && data.showCancel !== false">
{{ data.showSpinner ? 'Cancel' : 'Close' }}
</button>
</div>
Expand Down Expand Up @@ -98,6 +112,14 @@ export class GenericDialogComponent {
this.dialogRef.close();
}

onCancel(): void {
this.dialogRef.close(false);
}

onConfirm(): void {
this.dialogRef.close(true);
}

getTitleIcon(): string {
if (this.data.title?.includes('Successful')) {
return 'check_circle';
Expand Down
7 changes: 4 additions & 3 deletions src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,10 @@ export class AppComponent {
console.log('Run in browser');
}

// this.connectionService.isConnected$.subscribe(connection => {
// this.isConnected = connection.connected;
// });
// Subscribe to connection state changes
this.connectionService.isConnected$.subscribe(connection => {
this.isConnected = connection.connected;
});

const storedTheme = localStorage.getItem('theme');
if (storedTheme === 'dark') {
Expand Down
14 changes: 12 additions & 2 deletions src/app/home/home.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,18 @@ <h1>Welcome to the SailPoint UI Development Kit</h1>
<div class="form-row">
<mat-form-field appearance="outline" class="full-width">
<mat-label>Environment Name</mat-label>
<input matInput [(ngModel)]="config.tenantName" name="tenantName"
(input)="onTenantNameChange()" [disabled]="selectedTenant !== 'new'" required>
<input matInput [(ngModel)]="config.environmentName" name="environmentName"
placeholder="My Development Environment" [disabled]="selectedTenant !== 'new'" required>
<mat-hint>A friendly name for this environment configuration</mat-hint>
</mat-form-field>
</div>

<div class="form-row" *ngIf="selectedTenant === 'new'">
<mat-form-field appearance="outline" class="full-width">
<mat-label>Tenant Name (for URL generation)</mat-label>
<input matInput [(ngModel)]="config.tempTenantName" name="tempTenantName"
(input)="onTenantNameChange()" placeholder="your-tenant-name" required>
<mat-hint>Your SailPoint tenant identifier - used only to auto-generate URLs below</mat-hint>
</mat-form-field>
</div>

Expand Down
109 changes: 93 additions & 16 deletions src/app/home/home.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,12 @@
clientSecret: string | null;
name: string;
authType: string;
tenantName: string;
}

interface EnvironmentConfig {
tenantName: string;
environmentName: string;
tempTenantName?: string; // Only used during creation to auto-generate URLs
tenantUrl: string;
baseUrl: string;
authType: 'oauth' | 'pat';
Expand Down Expand Up @@ -84,7 +86,7 @@
oauthValidationStatus: 'unknown' | 'valid' | 'invalid' | 'testing' = 'unknown';

config: EnvironmentConfig = {
tenantName: '',
environmentName: '',
tenantUrl: '',
baseUrl: '',
authType: 'pat'
Expand Down Expand Up @@ -179,6 +181,10 @@
await this.setActiveEnvironment(this.actualTenant.name);
// Refresh tenant data to ensure we have the latest auth type
await this.refreshCurrentTenantAuthType();

// Auto-adjust auth method based on available credentials
await this.autoAdjustAuthMethod();

// Always load the selected environment's configuration
this.loadEnvironmentForEditing(this.actualTenant);
}
Expand Down Expand Up @@ -240,9 +246,11 @@
if (this.actualTenant.authType === 'oauth') {
// Perform OAuth authentication
await this.performOAuthConnection();
} else {
// Perform PAT authentication (default)
} else if (this.actualTenant.authType === 'pat') {
// Perform PAT authentication
await this.performPATConnection();
} else {
this.openErrorDialog('Invalid authentication method', 'Connection Error');
}
} catch (error) {
console.error('Error connecting to ISC:', error);
Expand Down Expand Up @@ -415,10 +423,10 @@

loadEnvironmentForEditing(tenant: Tenant): void {
this.config = {
tenantName: tenant.name,
environmentName: tenant.name,
tenantUrl: tenant.tenantUrl,
baseUrl: tenant.apiUrl,
authType: this.globalAuthMethod as 'oauth' | 'pat', // Use global auth method
authType: tenant.authType as 'oauth' | 'pat', // Use tenant's authType instead of global
clientId: tenant.clientId || '',
clientSecret: tenant.clientSecret || ''
};
Expand All @@ -428,7 +436,7 @@
this.oauthValidationStatus = 'unknown';

// Auto-validate OAuth if using OAuth method
if (this.globalAuthMethod === 'oauth' && this.config.baseUrl) {
if (this.config.authType === 'oauth' && this.config.baseUrl) {
void this.validateOAuthEndpoint();
}
}
Expand All @@ -438,7 +446,7 @@
const currentAuthType = await window.electronAPI.getGlobalAuthType();

this.config = {
tenantName: '',
environmentName: '',
tenantUrl: '',
baseUrl: '',
authType: currentAuthType as 'oauth' | 'pat'
Expand All @@ -447,10 +455,10 @@

onTenantNameChange() {
const isNewEnvironment = this.selectedTenant === 'new';
if (isNewEnvironment && this.config.tenantName) {
if (isNewEnvironment && this.config.tempTenantName) {
// Auto-generate URLs based on tenant name
this.config.tenantUrl = `https://${this.config.tenantName}.identitynow.com`;
this.config.baseUrl = `https://${this.config.tenantName}.api.identitynow.com`;
this.config.tenantUrl = `https://${this.config.tempTenantName}.identitynow.com`;
this.config.baseUrl = `https://${this.config.tempTenantName}.api.identitynow.com`;

// Trigger OAuth validation if using OAuth
if (this.globalAuthMethod === 'oauth') {
Expand Down Expand Up @@ -482,7 +490,7 @@
try {
const isUpdate = this.selectedTenant !== 'new';
const result = await window.electronAPI.createOrUpdateEnvironment({
environmentName: this.config.tenantName,
environmentName: this.config.environmentName,
tenantUrl: this.config.tenantUrl,
baseUrl: this.config.baseUrl,
authType: this.config.authType,
Expand Down Expand Up @@ -520,12 +528,14 @@
data: {
title: 'Confirm Deletion',
message: `Are you sure you want to delete the environment "${String(this.actualTenant.name)}"? This action cannot be undone.`,
showCancel: true
isConfirmation: true,
confirmText: 'Delete',
cancelText: 'Cancel'
}
});

dialogRef.afterClosed().subscribe((result) => {
if (result) {
if (result === true) {
void (async () => {
try {
const deleteResult = await window.electronAPI.deleteEnvironment(this.actualTenant!.name);
Expand All @@ -536,6 +546,8 @@
this.selectedTenant = 'new';
this.actualTenant = undefined;
this.showEnvironmentDetails = false;
this.isConnected = false;
this.connectionService.setConnectionState(false);
} else {
this.showError(String(deleteResult.error || 'Failed to delete environment'));
}
Expand Down Expand Up @@ -622,8 +634,14 @@
}

validateConfig(): boolean {
if (!this.config.tenantName.trim()) {
this.showError('Tenant name is required');
if (!this.config.environmentName.trim()) {
this.showError('Environment name is required');
return false;
}

// Only validate tempTenantName for new environments
if (this.selectedTenant === 'new' && !this.config.tempTenantName?.trim()) {
this.showError('Tenant name is required to generate URLs');
return false;
}

Expand Down Expand Up @@ -688,4 +706,63 @@
panelClass: ['success-snackbar']
});
}

async autoAdjustAuthMethod(): Promise<void> {
if (!this.actualTenant) return;

const currentAuthMethod = this.globalAuthMethod;
const hasPATCredentials = !!(this.actualTenant.clientId && this.actualTenant.clientSecret);

console.log(`Auto-adjusting auth method for ${this.actualTenant.name}:`);
console.log(`- Current method: ${currentAuthMethod}`);
console.log(`- Has PAT credentials: ${hasPATCredentials}`);

// If PAT is selected but credentials are missing, switch to OAuth
if (currentAuthMethod === 'pat' && !hasPATCredentials) {
console.log('PAT selected but credentials missing, switching to OAuth');
this.globalAuthMethod = 'oauth';
await window.electronAPI.setGlobalAuthType('oauth');
this.actualTenant.authType = 'oauth';
this.showSuccess(`Switched to OAuth authentication for ${this.actualTenant.name} (PAT credentials not configured)`);
return;
}

// If OAuth is selected, validate the endpoint
if (currentAuthMethod === 'oauth') {
// Set up temporary config to test OAuth endpoint
const tempConfig = {
baseUrl: this.actualTenant.apiUrl
};

try {
const oauthInfoUrl = `${tempConfig.baseUrl}/oauth/info`;
const response = await fetch(oauthInfoUrl, {
method: 'GET',
headers: { 'Accept': 'application/json' }
});

// If OAuth endpoint is not reachable and we have PAT credentials, switch to PAT
if (!response.ok && hasPATCredentials) {
console.log('OAuth endpoint not reachable but PAT credentials available, switching to PAT');
this.globalAuthMethod = 'pat';
await window.electronAPI.setGlobalAuthType('pat');
this.actualTenant.authType = 'pat';
this.showSuccess(`Switched to PAT authentication for ${this.actualTenant.name} (OAuth endpoint not reachable)`);
return;
}
} catch (error) {

Check failure on line 753 in src/app/home/home.component.ts

View workflow job for this annotation

GitHub Actions / build (20)

'error' is defined but never used

Check failure on line 753 in src/app/home/home.component.ts

View workflow job for this annotation

GitHub Actions / build (20)

'error' is defined but never used

Check failure on line 753 in src/app/home/home.component.ts

View workflow job for this annotation

GitHub Actions / build (20)

'error' is defined but never used
// If OAuth endpoint test fails and we have PAT credentials, switch to PAT
if (hasPATCredentials) {
console.log('OAuth endpoint test failed but PAT credentials available, switching to PAT');
this.globalAuthMethod = 'pat';
await window.electronAPI.setGlobalAuthType('pat');
this.actualTenant.authType = 'pat';
this.showSuccess(`Switched to PAT authentication for ${this.actualTenant.name} (OAuth endpoint not available)`);
return;
}
}
}

console.log(`Keeping current auth method: ${currentAuthMethod}`);
}
}
Loading