Skip to content

Commit

Permalink
Merge pull request #79 from yannick-beot-sp/feature/read-only
Browse files Browse the repository at this point in the history
⭐ Lock tenant as read-only to prevent any change (cf. #75)
  • Loading branch information
yannick-beot-sp committed Apr 26, 2024
2 parents 576d6cc + f5b24c8 commit d2834da
Show file tree
Hide file tree
Showing 24 changed files with 168 additions and 35 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ This changelog is following the recommended format by [keepachangelog](https://k

- Add searching and viewing identities by [@henrique-quintino-sp](https://github.com/henrique-quintino-sp) (cf. [#74](https://github.com/yannick-beot-sp/vscode-sailpoint-identitynow/pull/74))
- Add attribute sync, process and delete command on identities by [@henrique-quintino-sp](https://github.com/henrique-quintino-sp) (cf. [#74](https://github.com/yannick-beot-sp/vscode-sailpoint-identitynow/pull/74))
- Lock tenant as read-only to prevent any change (cf. [#75](https://github.com/yannick-beot-sp/vscode-sailpoint-identitynow/issues/75))

### Fixed

Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,7 @@ The patterns defined above use the following tokens:
- Add searching and viewing identities by [@henrique-quintino-sp](https://github.com/henrique-quintino-sp) (cf. [#74](https://github.com/yannick-beot-sp/vscode-sailpoint-identitynow/pull/74))
- Add attribute sync, process and delete command on identities by [@henrique-quintino-sp](https://github.com/henrique-quintino-sp) (cf. [#74](https://github.com/yannick-beot-sp/vscode-sailpoint-identitynow/pull/74))
- Fixed normalizeNames (cf. [#73](https://github.com/yannick-beot-sp/vscode-sailpoint-identitynow/issues/73))
- Lock tenant as read-only to prevent any change (cf. [#75](https://github.com/yannick-beot-sp/vscode-sailpoint-identitynow/issues/75))

## 1.2.0

Expand Down
52 changes: 44 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,16 @@
"title": "ISC: Add tenant...",
"icon": "$(add)"
},
{
"command": "vscode-sailpoint-identitynow.tenant.set-writable",
"title": "Read-only",
"icon": "$(lock)"
},
{
"command": "vscode-sailpoint-identitynow.tenant.set-readonly",
"title": "writable",
"icon": "$(unlock)"
},
{
"command": "vscode-sailpoint-identitynow.remove-tenant",
"title": "Remove tenant"
Expand Down Expand Up @@ -126,6 +136,10 @@
"title": "ISC: Import config...",
"icon": "$(import)"
},
{
"command": "vscode-sailpoint-identitynow.modified-resource",
"title": "Modified resource"
},
{
"command": "vscode-sailpoint-identitynow.refresh-forced",
"title": "ISC: Refresh",
Expand Down Expand Up @@ -514,10 +528,22 @@
],
"menus": {
"commandPalette": [
{
"command": "vscode-sailpoint-identitynow.modified-resource",
"when": "never"
},
{
"command": "vscode-sailpoint-identitynow.refresh",
"when": "never"
},
{
"command": "vscode-sailpoint-identitynow.tenant.set-readonly",
"when": "never"
},
{
"command": "vscode-sailpoint-identitynow.tenant.set-writable",
"when": "never"
},
{
"command": "vscode-sailpoint-identitynow.rename-tenant",
"when": "never"
Expand Down Expand Up @@ -850,38 +876,48 @@
},
{
"command": "vscode-sailpoint-identitynow.remove-tenant",
"when": "view == vscode-sailpoint-identitynow.view && viewItem == tenant"
"when": "view == vscode-sailpoint-identitynow.view && viewItem =~ /^tenant/"
},
{
"command": "vscode-sailpoint-identitynow.tenant.set-readonly",
"when": "view == vscode-sailpoint-identitynow.view && viewItem == tenantWritable",
"group": "inline@1"
},
{
"command": "vscode-sailpoint-identitynow.tenant.set-writable",
"when": "view == vscode-sailpoint-identitynow.view && viewItem == tenantReadOnly",
"group": "inline@1"
},
{
"command": "vscode-sailpoint-identitynow.rename-tenant",
"when": "view == vscode-sailpoint-identitynow.view && viewItem == tenant"
"when": "view == vscode-sailpoint-identitynow.view && viewItem =~ /^tenant/"
},
{
"command": "vscode-sailpoint-identitynow.tenant.edit.public-identities-config",
"when": "view == vscode-sailpoint-identitynow.view && viewItem == tenant"
"when": "view == vscode-sailpoint-identitynow.view && viewItem =~ /^tenant/"
},
{
"command": "vscode-sailpoint-identitynow.tenant.edit.access-request-config",
"when": "view == vscode-sailpoint-identitynow.view && viewItem == tenant"
"when": "view == vscode-sailpoint-identitynow.view && viewItem =~ /^tenant/"
},
{
"command": "vscode-sailpoint-identitynow.tenant.edit.password-org-config",
"when": "view == vscode-sailpoint-identitynow.view && viewItem == tenant",
"when": "view == vscode-sailpoint-identitynow.view && viewItem =~ /^tenant/",
"group": "password"
},
{
"command": "vscode-sailpoint-identitynow.tenant.generate-digit-token",
"when": "view == vscode-sailpoint-identitynow.view && viewItem == tenant",
"when": "view == vscode-sailpoint-identitynow.view && viewItem =~ /^tenant/",
"group": "password"
},
{
"command": "vscode-sailpoint-identitynow.export-config.view",
"when": "view == vscode-sailpoint-identitynow.view && viewItem == tenant",
"when": "view == vscode-sailpoint-identitynow.view && viewItem =~ /^tenant/",
"group": "config"
},
{
"command": "vscode-sailpoint-identitynow.import-config.view",
"when": "view == vscode-sailpoint-identitynow.view && viewItem == tenant",
"when": "view == vscode-sailpoint-identitynow.view && viewItem =~ /^tenant/",
"group": "config"
},
{
Expand Down
3 changes: 2 additions & 1 deletion src/ISCUriHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ export class ISCUriHandler implements vscode.UriHandler {
id: tenantId,
name: q.displayName ?? q.tenantName,
tenantName: normalizedTenantName,
authenticationMethod: (q.authenticationMethod.toLowerCase() === "accesstoken" ? AuthenticationMethod.accessToken : AuthenticationMethod.personalAccessToken)
authenticationMethod: (q.authenticationMethod.toLowerCase() === "accesstoken" ? AuthenticationMethod.accessToken : AuthenticationMethod.personalAccessToken),
readOnly: true
});
}
if (q.authenticationMethod.toLowerCase() === "accesstoken") {
Expand Down
2 changes: 1 addition & 1 deletion src/commands/NewAttributeSearchConfigCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export class NewAttributeSearchConfigCommand {

// if the command is called from the Tree View
if (node !== undefined && node instanceof SearchAttributesTreeItem) {
context["tenant"] = await this.tenantService.getTenant(node.tenantId);
context["tenant"] = this.tenantService.getTenant(node.tenantId);
}

let client: ISCClient | undefined = undefined;
Expand Down
2 changes: 1 addition & 1 deletion src/commands/access-profile/NewAccessProfileCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export class NewAccessProfileCommand {
const context: WizardContext = {};
// if the command is called from the Tree View
if (accessProfilesTreeItem !== undefined && accessProfilesTreeItem instanceof AccessProfilesTreeItem) {
context["tenant"] = await this.tenantService.getTenant(accessProfilesTreeItem.tenantId);
context["tenant"] = this.tenantService.getTenant(accessProfilesTreeItem.tenantId);
}

let client: ISCClient | undefined = undefined;
Expand Down
3 changes: 2 additions & 1 deletion src/commands/addTenant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@ export class AddTenantCommand {
id: tenantId,
name: displayName,
tenantName: normalizedTenantName,
authenticationMethod: authMethod
authenticationMethod: authMethod,
readOnly: true
});
try {
const session: vscode.AuthenticationSession = await vscode.authentication.getSession(
Expand Down
3 changes: 3 additions & 0 deletions src/commands/constants.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
const COMMAND_PREFIX = 'vscode-sailpoint-identitynow';
export const OPEN_RESOURCE = `${COMMAND_PREFIX}.open-resource`;
export const REMOVE_RESOURCE = `${COMMAND_PREFIX}.remove-resource`;
export const MODIFIED_RESOURCE = `${COMMAND_PREFIX}.modified-resource`;
export const REFRESH_FORCED = `${COMMAND_PREFIX}.refresh-forced`;
export const REFRESH = `${COMMAND_PREFIX}.refresh`;
export const ADD_TENANT = `${COMMAND_PREFIX}.add-tenant`;
export const TENANT_SET_READONLY = `${COMMAND_PREFIX}.tenant.set-readonly`;
export const TENANT_SET_WRITABLE = `${COMMAND_PREFIX}.tenant.set-writable`;
export const RENAME_TENANT = `${COMMAND_PREFIX}.rename-tenant`;
export const REMOVE_TENANT = `${COMMAND_PREFIX}.remove-tenant`;
export const EXPORT_CONFIG_VIEW = `${COMMAND_PREFIX}.export-config.view`;
Expand Down
2 changes: 1 addition & 1 deletion src/commands/renameTenant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export class RenameTenantCommand {
if (isEmpty(displayName)) {
return;
}
const tenantInfo = await this.tenantService.getTenant(node.tenantId);
const tenantInfo = this.tenantService.getTenant(node.tenantId);
if (tenantInfo) {
tenantInfo.name = displayName;
await this.tenantService.setTenant(tenantInfo);
Expand Down
2 changes: 1 addition & 1 deletion src/commands/role/NewRoleCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export class NewRoleCommand {

// if the command is called from the Tree View
if (rolesTreeItem !== undefined && rolesTreeItem instanceof RolesTreeItem) {
context["tenant"] = await this.tenantService.getTenant(rolesTreeItem.tenantId);
context["tenant"] = this.tenantService.getTenant(rolesTreeItem.tenantId);
}

let client: ISCClient | undefined = undefined;
Expand Down
2 changes: 1 addition & 1 deletion src/commands/source/CloneSourceCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ export class CloneSourceCommand {

// if the command is called from the Tree View
if (node !== undefined && node instanceof SourceTreeItem) {
context["tenant"] = await this.tenantService.getTenant(node.tenantId);
context["tenant"] = this.tenantService.getTenant(node.tenantId);
context["source"] = {
id: node.id!,
name: node.label!
Expand Down
2 changes: 1 addition & 1 deletion src/commands/source/PeekSourceCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export class PeekSourceCommand {

// if the command is called from the Tree View
if (node !== undefined && node instanceof SourceTreeItem) {
context["tenant"] = await this.tenantService.getTenant(node.tenantId);
context["tenant"] = this.tenantService.getTenant(node.tenantId);
context["source"] = {
id: node.id!,
name: node.label!
Expand Down
2 changes: 1 addition & 1 deletion src/commands/source/PingClusterCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export class PingClusterCommand {

// if the command is called from the Tree View
if (node !== undefined && node instanceof SourceTreeItem) {
context["tenant"] = await this.tenantService.getTenant(node.tenantId);
context["tenant"] = this.tenantService.getTenant(node.tenantId);
context["source"] = {
id: node.id!,
name: node.label!
Expand Down
2 changes: 1 addition & 1 deletion src/commands/source/TestConnectionCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export class TestConnectionCommand {

// if the command is called from the Tree View
if (node !== undefined && node instanceof SourceTreeItem) {
context["tenant"] = await this.tenantService.getTenant(node.tenantId);
context["tenant"] = this.tenantService.getTenant(node.tenantId);
context["source"] = {
id: node.id!,
name: node.label!
Expand Down
2 changes: 1 addition & 1 deletion src/commands/tenant/generateDigitTokenCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export class GenerateDigitTokenCommand {

// if the command is called from the Tree View
if (node !== undefined && node instanceof TenantTreeItem) {
context["tenant"] = await this.tenantService.getTenant(node.tenantId);
context["tenant"] = this.tenantService.getTenant(node.tenantId);
}
let client: ISCClient | undefined = undefined;

Expand Down
43 changes: 43 additions & 0 deletions src/commands/tenant/tenantReadOnlyConfigCommand.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { TenantTreeItem } from "../../models/ISCTreeItem";
import * as vscode from 'vscode';
import * as commands from '../constants';
import { TenantService } from "../../services/TenantService";

export class TenantReadOnlyConfigCommand {

constructor(private readonly tenantService: TenantService) {

}

public async setReadOnly(node: TenantTreeItem): Promise<void> {
console.log("> TenantReadOnlyConfigCommand.setReadOnly", node);
await this.updateReadonly(node, true)
}

public async setWritable(node: TenantTreeItem): Promise<void> {
console.log("> TenantReadOnlyConfigCommand.setWritable", node);
await this.updateReadonly(node, false)
}

private async updateReadonly(node: TenantTreeItem, readOnly: boolean): Promise<void> {
const tenantInfo = this.tenantService.getTenant(node.tenantId)
tenantInfo.readOnly = readOnly
this.tenantService.setTenant(tenantInfo)

// force refresh to update icon
await vscode.commands.executeCommand(commands.REFRESH_FORCED);

const uris = vscode.window.tabGroups?.all?.
flatMap(tabGroup => tabGroup.tabs)?.
map(tab => tab.input).
map(input => typeof input === 'object' && input !== null && 'uri' in input ? input.uri : undefined)
.filter(Boolean)
.filter((uri: vscode.Uri) => uri.authority === tenantInfo.tenantName)
console.log(uris);

if (uris && uris.length > 0) {
await vscode.commands.executeCommand(commands.MODIFIED_RESOURCE, uris);
}

}
}
17 changes: 15 additions & 2 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ import { onErrorResponse, onRequest, onResponse } from './services/AxiosHandlers
import axios from 'axios';
import { OpenScriptCommand } from './commands/rule/openScriptCommand';
import { IdentityTreeViewCommand } from './commands/identity/IdentityTreeViewCommand';
import { TenantReadOnlyConfigCommand } from './commands/tenant/tenantReadOnlyConfigCommand';

// this method is called when your extension is activated
// your extension is activated the very first time the command is executed
Expand Down Expand Up @@ -153,6 +154,15 @@ export function activate(context: vscode.ExtensionContext) {
vscode.commands.registerCommand(commands.RESET_SOURCE_ENTITLEMENTS,
(tenantTreeItem) => treeManager.resetEntitlements(tenantTreeItem)));
const testConnectionCommand = new TestConnectionCommand(tenantService);

const tenantReadOnlyConfigCommand = new TenantReadOnlyConfigCommand(tenantService)
context.subscriptions.push(
vscode.commands.registerCommand(commands.TENANT_SET_READONLY,
tenantReadOnlyConfigCommand.setReadOnly, tenantReadOnlyConfigCommand))
context.subscriptions.push(
vscode.commands.registerCommand(commands.TENANT_SET_WRITABLE,
tenantReadOnlyConfigCommand.setWritable, tenantReadOnlyConfigCommand))

context.subscriptions.push(
vscode.commands.registerCommand(commands.TEST_SOURCE,
testConnectionCommand.execute, testConnectionCommand));
Expand Down Expand Up @@ -290,12 +300,15 @@ export function activate(context: vscode.ExtensionContext) {
vscode.commands.registerCommand(commands.IMPORT_CONFIG_VIEW,
treeviewImporterCommand.execute, treeviewImporterCommand));

const iscClientResourceProvider = new ISCResourceProvider(tenantService)
context.subscriptions.push(
vscode.workspace.registerFileSystemProvider(
URL_PREFIX,
new ISCResourceProvider(tenantService)
iscClientResourceProvider
));

context.subscriptions.push(
vscode.commands.registerCommand(commands.MODIFIED_RESOURCE,
iscClientResourceProvider.triggerModified, iscClientResourceProvider));
const newTransformCommand = new NewTransformCommand();
context.subscriptions.push(
vscode.commands.registerCommand(commands.NEW_TRANSFORM,
Expand Down
25 changes: 22 additions & 3 deletions src/files/ISCResourceProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,16 @@ export class ISCResourceProvider implements FileSystemProvider {
const data = await this.lookupResource(uri);
const id = getIdByUri(uri);
const resourcePath = getPathByUri(uri);
// const isFile = this.isUUID(id);
const tenantName = uri.authority;
const tenantInfo = await this.tenantService.getTenantByTenantName(tenantName)
const isReadOnly = tenantInfo && tenantInfo.readOnly
const isFile = id !== "provisioning-policies" && id !== "schemas";
return {
type: (isFile ? FileType.File : FileType.Directory),
ctime: toTimestamp(data.created),
mtime: toTimestamp(data.modified),
size: convertToText(data).length,
permissions: resourcePath.match("identities") ? vscode.FilePermission.Readonly : null
permissions: isReadOnly || resourcePath.match("identities") ? vscode.FilePermission.Readonly : null
};
}
readDirectory(
Expand Down Expand Up @@ -102,7 +104,7 @@ export class ISCResourceProvider implements FileSystemProvider {
null,
true
);
if (response.data.length ===1 ) {
if (response.data.length === 1) {
data = response.data[0]
}
} else {
Expand Down Expand Up @@ -132,6 +134,13 @@ export class ISCResourceProvider implements FileSystemProvider {
const tenantInfo = await this.tenantService.getTenantByTenantName(
tenantName
);

// Additional check just in case
if (tenantInfo.readOnly) {
throw new Error("Tenant is read-only");

}

const client = new ISCClient(tenantInfo?.id ?? "", tenantName);
let data = uint8Array2Str(content);

Expand Down Expand Up @@ -274,11 +283,21 @@ export class ISCResourceProvider implements FileSystemProvider {
delete(uri: Uri, options: { recursive: boolean }): void | Thenable<void> {
throw new Error("Method delete not implemented.");
}

rename(
oldUri: Uri,
newUri: Uri,
options: { overwrite: boolean }
): void | Thenable<void> {
throw new Error("Method rename not implemented.");
}

public triggerModified(uris: vscode.Uri | vscode.Uri[]) {
if (uris === undefined) { return }

if (!Array.isArray(uris)) {
uris = [uris]
}
uris.forEach(uri => this._emitter.fire([{ type: vscode.FileChangeType.Changed, uri }]), this)
}
}
Loading

0 comments on commit d2834da

Please sign in to comment.