Skip to content

Commit

Permalink
Merge branch '238-encryption-api-methods' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
countnazgul committed Dec 20, 2023
2 parents acff9d5 + bbab2ab commit 71ec62d
Show file tree
Hide file tree
Showing 7 changed files with 269 additions and 12 deletions.
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,19 @@

All notable changes to this project will be documented in this file.

## 0.17.0 - 2023-12-20

- `Encryption` - new API endpoints implementation [post](https://qlik.dev/changelog/75-encryption-api/)
- dependency update

## 0.16.0 - 2023-12-17

- dependency updates
- new tabular reporting related endpoints implementations

## 0.15.0 - 2023-12-09

- - `AutomationConnections` - new API endpoints implementation [post](https://qlik.dev/changelog/68-api-updates-automation-connections/)
- `AutomationConnections` - new API endpoints implementation [post](https://qlik.dev/changelog/68-api-updates-automation-connections/)
- dependency updates

## 0.14.6 - 2023-09-20
Expand Down
18 changes: 9 additions & 9 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "qlik-saas-api",
"version": "0.16.0",
"version": "0.17.0",
"description": "Interact with Qlik Sense SaaS REST API",
"author": {
"email": "info@informatiqal.com",
Expand Down Expand Up @@ -60,7 +60,7 @@
"devDependencies": {
"@rollup/plugin-typescript": "^11.1.5",
"@types/form-data": "^2.5.0",
"@types/node": "20.10.4",
"@types/node": "20.10.5",
"dotenv": "16.3.1",
"esm": "^3.2.25",
"nyc": "15.1.0",
Expand Down
3 changes: 3 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { Reloads } from "./modules/Reloads";
import { ReloadTasks } from "./modules/ReloadTasks";
import { Roles } from "./modules/Roles";
import { Users } from "./modules/Users";
import { Encryption } from "./modules/Encryption";
import { WebHooks } from "./modules/WebHooks";
import { OAuthTokens } from "./modules/OAuthTokens";
import { Collections } from "./modules/Collections";
Expand Down Expand Up @@ -76,6 +77,7 @@ export namespace QlikSaaSApi {
public roles: Roles;
public transports: Transports;
public users: Users;
public encryption: Encryption;
public webHooks: WebHooks;
public oauthTokens: OAuthTokens;
public collections: Collections;
Expand Down Expand Up @@ -115,6 +117,7 @@ export namespace QlikSaaSApi {
this.roles = new Roles(this.#saasClient);
this.transports = new Transports(this.#saasClient);
this.users = new Users(this.#saasClient);
this.encryption = new Encryption(this.#saasClient);
this.webHooks = new WebHooks(this.#saasClient);
this.oauthTokens = new OAuthTokens(this.#saasClient);
this.collections = new Collections(this.#saasClient);
Expand Down
113 changes: 113 additions & 0 deletions src/modules/Encryption.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import { QlikSaaSClient } from "qlik-rest-api";
import { IKeyProvider, KeyProvider } from "./KeyProvider";
import { KeepRequired } from "../types/types";

export interface IMigrationInformation {
/**
* Migration operation ID
*/
id: string;
/**
* Migration operation state
*/
state: "New" | "InProgress" | "Completed";
/**
* Progress in percentage
*/
progress: number;
tenantId: string;
completedAt: string;
initiatedAt: string;
/**
* The nre key ARN that keys should be migrated to
*/
migratingTo: string;
/**
* The key ARN bing migrated from (in case of QlikVault, could be a short name only)
*/
migratingFrom: string;
/**
* The nre key prefix (to help services know which prefix should NOT be migrated)
*/
migratingToPrefix: string;
/**
* The nre ARN fingerprint
*/
migratingToFingerprint: string;
}

export class Encryption {
#saasClient: QlikSaaSClient;
constructor(saasClient: QlikSaaSClient) {
this.#saasClient = saasClient;
}

async get(arg: { id: string }) {
if (!arg.id) throw new Error(`encryption.get: "id" parameter is required`);

const kp: KeyProvider = new KeyProvider(this.#saasClient, arg.id);
await kp.init();

return kp;
}

/**
* Lists keyproviders registered for the tenant
*/
async getAll() {
return await this.#saasClient
.Get<IKeyProvider[]>(`encryption/keyproviders?limit=50`)
.then((res) => res.data)
.then((data) =>
data.map((t) => new KeyProvider(this.#saasClient, t.arnFingerPrint, t))
);
}

/**
* Lists keyproviders registered for the tenant [Qlik, AWS-KMS]
*/
async list() {
return await this.#saasClient
.Get<IKeyProvider[]>(`encryption/keyproviders/actions/list?limit=50`)
.then((res) => res.data)
.then((data) =>
data.map((t) => new KeyProvider(this.#saasClient, t.arnFingerPrint, t))
);
}

/**
* Gets ongoing migration details
*/
async migrationDetails() {
return await this.#saasClient
.Get<IMigrationInformation[]>(
`encryption/keyproviders/migration/actions/details?limit=50`
)
.then((res) => res.data);
}

/**
* Registers an AWS-KMS key for the specific tenant
*/
async create(arg: KeepRequired<IKeyProvider, "arn" | "name">) {
if (!arg.arn)
throw new Error(`encryption.create: "arn" parameter is required`);
if (!arg.name)
throw new Error(`encryption.create: "name" parameter is required`);

return await this.#saasClient
.Post<IKeyProvider>(`encryption/keyproviders`, arg)
.then((res) => new KeyProvider(this.#saasClient, res.data.arn, res.data));
}

/**
* Resets tenant key provider to Qlik managed provider
*/
async resetToDefaultProvider() {
return await this.#saasClient
.Get<IMigrationInformation>(
`keyproviders/actions/reset-to-default-provider`
)
.then((res) => res.data);
}
}
131 changes: 131 additions & 0 deletions src/modules/KeyProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import { QlikSaaSClient } from "qlik-rest-api";
import { IMigrationInformation } from "./Encryption";

export interface IKeyProvider {
/**
* The provider resource notation for the key
*/
arn: string;
/**
* Name of key provider entry
*/
name: string;
/**
* Indicates whether the key is being used to encrypt/decrypt secrets
*/
current: boolean;
/**
* Tenant ID
*/
tenantId: string;
/**
* When key entry was created
*/
createdAt: string;
/**
* Description of key provider entry
*/
description: string;
/**
* Key Provider type.
*
* At the moment only "AWS-KMS" (12/2023)
*/
keyProvider: string;
/**
* Indicated whether the key has multi-region configuration and has replica key in qcs secondary region
*/
multiRegion: boolean;
replicaKeys: {
/**
* Replica key keeps list of backup keys from the supported qcs secondary region
*/
arn: string;
/**
* Region indicates the backup qcs-region link to the primary region
*/
region: string;
}[];
/**
* The ARN fingerprint
*/
arnFingerPrint: string;
/**
* When the key was promoted to being current active one
*/
promotedToCurrentAt: string;
/**
* When the ley was demoted from being current to non active
*/
demotedFromCurrentAt: string;
}

export class KeyProvider {
#id: string;
#saasClient: QlikSaaSClient;
details: IKeyProvider;
constructor(saasClient: QlikSaaSClient, id: string, details?: IKeyProvider) {
if (!id) throw new Error(`keyProvider.get: "id" parameter is required`);

this.details = details ?? ({} as IKeyProvider);
this.#id = id;
this.#saasClient = saasClient;
}

async init(arg?: { force: boolean }) {
if (
!this.details ||
Object.keys(this.details).length == 0 ||
arg?.force == true
) {
this.details = await this.#saasClient
.Get<IKeyProvider>(`encryption/keyproviders/${this.#id}`)
.then((res) => res.data);
}
}

async remove() {
return await this.#saasClient
.Delete(`encryption/keyproviders/${this.#id}`)
.then((res) => res.status);
}

/**
* Patches Name & Description of keyprovider information
*/
async patch(arg: { op: string; path: string; value: string }[]) {
let updateStatus = 0;

return await this.#saasClient
.Patch(`encryption/keyproviders/${this.#id}`, arg)
.then((res) => {
updateStatus = res.status;
return this.init({ force: true });
})
.then(() => updateStatus);
}

/**
* Migrates existing cipher keys from current key provider to requested key provider
*/
async migrate() {
return await this.#saasClient
.Post<IMigrationInformation>(
`encryption/keyproviders/${this.#id}/actions/migrate`,
{}
)
.then((res) => res.data);
}

/**
* Validates AWS-KMS key access and usage
*/
async test() {
return await this.#saasClient
.Post<IKeyProvider>(
`encryption/keyproviders/${this.#id}/actions/test`,
{}
)
.then((res) => res.data);
}
}
5 changes: 5 additions & 0 deletions src/types/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,8 @@ export interface IEntityRemove {
}

export type Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>;

export type Required<T, K extends keyof T> = T & { [P in K]-?: T[P] };

export type KeepRequired<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>> &
Required<T, K>;

0 comments on commit 71ec62d

Please sign in to comment.