Skip to content

Commit

Permalink
Add user and group private key rotation (#41)
Browse files Browse the repository at this point in the history
* #37 Add ability to create users with needs rotation and rotate th… (#39)

* #37 Add ability to create users with needs rotation and rotate their key

* Remove unused error code

* #37 Add key rotation for groups and change init response to retur… (#40)

* #37 Add key rotation for groups and change init response to return info about current user for rotation purposes

* Expand details in comment

* Add missing test, bump up test coverage leniency for Recrypt tests
  • Loading branch information
Ernie Turner committed Mar 4, 2020
1 parent ffd4620 commit 6911af9
Show file tree
Hide file tree
Showing 41 changed files with 1,716 additions and 863 deletions.
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ SDK for using IronCore Labs from your NodeJS server side applications. Read [our

## Supported Platforms

| | Node 8 | Node 10 | Node 12 |
| ------------------- | ------ | ------- | ------- |
| Linux x64 glibc || ||
| Linux x64 musl-libc || ||
| OSX x64 || ||
| | Node 10 | Node 12 |
| ------------------- | ------- | ------- |
| Linux x64 glibc |||
| Linux x64 musl-libc |||
| OSX x64 |||

## Installation

Expand Down
22 changes: 19 additions & 3 deletions integration/Groups.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as inquirer from "inquirer";
import {SDK, GroupDetailResponse} from "../ironnode";
import {GroupDetailResponse, SDK} from "../ironnode";
import {log} from "./Logger";

/**
Expand Down Expand Up @@ -73,7 +73,7 @@ export function get(IronNode: SDK) {
*/
export function create(IronNode: SDK) {
return inquirer
.prompt<{id: string; name: string; addAsMember: boolean}>([
.prompt<{id: string; name: string; addAsMember: boolean; needsRotation: boolean}>([
{
name: "id",
type: "input",
Expand All @@ -89,12 +89,19 @@ export function create(IronNode: SDK) {
type: "confirm",
message: "Add yourself as a member? ",
},
{
name: "needsRotation",
type: "confirm",
message: "Create with needs rotation?",
default: false,
},
])
.then(({id, name, addAsMember}) => {
.then(({id, name, addAsMember, needsRotation}) => {
const options = {
groupID: id || undefined,
groupName: name || undefined,
addAsMember,
needsRotation,
};
return IronNode.group.create(options);
})
Expand All @@ -118,6 +125,15 @@ export function update(IronNode: SDK) {
.then(log);
}

/**
* Rotate an existing groups private key
*/
export function rotatePrivateKey(IronNode: SDK) {
return getFormattedGroupList(IronNode, true)
.then(({id}) => IronNode.group.rotatePrivateKey(id))
.then(log);
}

/**
* Add admins to a group that the user is an admin of.
*/
Expand Down
16 changes: 16 additions & 0 deletions integration/Users.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,22 @@ export function publicKeyLookup(IronNode: SDK) {
.then(log);
}

/**
* Ask for the users escrow password and use it to rotate the users master private key.
*/
export function rotateMasterKey(IronNode: SDK) {
return inquirer
.prompt<{escrowPassword: string}>([
{
name: "escrowPassword",
type: "password",
message: "Enter accounts escrow password:",
},
])
.then(({escrowPassword}) => IronNode.user.rotateMasterKey(escrowPassword))
.then(log);
}

/**
* Get a users devices and display the results
*/
Expand Down
14 changes: 10 additions & 4 deletions integration/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* tslint:disable no-console*/
import * as fs from "fs";
import * as path from "path";
import * as inquirer from "inquirer";
import * as path from "path";
import {initializeSDKWithLocalDevice} from "./sdkOperation";
import {askForUserOperation} from "./userOperation";

Expand All @@ -22,14 +22,20 @@ if (hasLocalDevice) {
type: "list",
name: "useDevice",
message: "Local device keys found, use them?",
choices: [{name: "Yes", value: true}, {name: "No", value: false}],
choices: [
{name: "Yes", value: true},
{name: "No", value: false},
],
})
.then(({useDevice}) => {
if (useDevice) {
return initializeSDKWithLocalDevice();
}
return askForUserOperation("Pick a user operation to run.").then(initializeSDKWithLocalDevice);
});
})
.catch((e) => console.error(e));
} else {
askForUserOperation("No local device found, pick a user operation to run.").then(initializeSDKWithLocalDevice);
askForUserOperation("No local device found, pick a user operation to run.")
.then(initializeSDKWithLocalDevice)
.catch((e) => console.error(e));
}
18 changes: 14 additions & 4 deletions integration/sdkOperation.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
/* tslint:disable no-console cyclomatic-complexity*/
import * as path from "path";
import * as inquirer from "inquirer";
import * as path from "path";
import {SDK} from "../ironnode";
import {initialize} from "../src/index";
import * as Documents from "./Documents";
import * as Groups from "./Groups";
import {log} from "./Logger";
import * as Users from "./Users";
import {SDK} from "../ironnode";

const topLevelPrompt: inquirer.ListQuestion<{operation: string}> = {
type: "list",
Expand All @@ -27,6 +28,7 @@ const topLevelPrompt: inquirer.ListQuestion<{operation: string}> = {
{name: "Group Get", value: "groupGet"},
{name: "Group Create", value: "groupCreate"},
{name: "Group Update", value: "groupUpdate"},
{name: "Group Private Key Rotate", value: "groupRotate"},
{name: "Group Add Admins", value: "groupAddAdmins"},
{name: "Group Remove Admins", value: "groupRemoveAdmins"},
{name: "Group Add Members", value: "groupAddMembers"},
Expand All @@ -36,6 +38,7 @@ const topLevelPrompt: inquirer.ListQuestion<{operation: string}> = {
{name: "User Public Key Lookup", value: "userKeyLookup"},
{name: "User Device List", value: "userDeviceList"},
{name: "User Device Delete", value: "userDeviceDelete"},
{name: "User Rotate Master Private Key", value: "rotateUserKey"},
new inquirer.Separator(),
{name: "Quit", value: "quit"},
new inquirer.Separator(),
Expand Down Expand Up @@ -73,6 +76,8 @@ function routeAnswerToOperation(IronNode: SDK, answer: string) {
return Groups.create(IronNode);
case "groupUpdate":
return Groups.update(IronNode);
case "groupRotate":
return Groups.rotatePrivateKey(IronNode);
case "groupAddAdmins":
return Groups.addAdmins(IronNode);
case "groupRemoveAdmins":
Expand All @@ -89,6 +94,8 @@ function routeAnswerToOperation(IronNode: SDK, answer: string) {
return Users.deviceList(IronNode);
case "userDeviceDelete":
return Users.deviceDelete(IronNode);
case "rotateUserKey":
return Users.rotateMasterKey(IronNode);
case "quit":
return process.exit();
default:
Expand All @@ -107,7 +114,7 @@ function askForOperation(IronNode: SDK): Promise<void> {
.then(({operation}) => {
return routeAnswerToOperation(IronNode, operation).catch((error) => {
console.log("\x1Bc");
console.error(`${error.message}\n\n`);
console.error(`${error}\n\n`);
//Even if an error occurs, recover and go back to the operation list
return Promise.resolve();
});
Expand All @@ -121,6 +128,9 @@ function askForOperation(IronNode: SDK): Promise<void> {
export function initializeSDKWithLocalDevice() {
const Config = require(path.join(__dirname, "./.device.json"));
return initialize(Config.accountID, Config.segmentID, Config.deviceKeys.privateKey, Config.signingKeys.privateKey)
.then((IronNode) => askForOperation(IronNode))
.then((IronNode) => {
log(IronNode.userContext);
return askForOperation(IronNode);
})
.catch((error) => console.error(`SDK Initialization Error: ${error.message}`));
}
16 changes: 10 additions & 6 deletions integration/userOperation.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
/* tslint:disable no-console*/
import * as fs from "fs";
import * as path from "path";
import * as inquirer from "inquirer";
import * as jwt from "jsonwebtoken";
import * as path from "path";
import {User} from "../src/index";
import {logWithMessage, log} from "./Logger";
import {log, logWithMessage} from "./Logger";
// tslint:disable-next-line
const Config = require("./project.json");
const keyFile = path.join(__dirname, "./private.key");
Expand Down Expand Up @@ -49,7 +49,7 @@ function verifyUser() {
*/
function createUser() {
return inquirer
.prompt<{userID: string; password: string}>([
.prompt<{userID: string; password: string; needsRotation: boolean}>([
{
type: "input",
name: "userID",
Expand All @@ -60,10 +60,14 @@ function createUser() {
name: "password",
message: "Input password to escrow users private key: ",
},
{
type: "confirm",
default: false,
name: "needsRotation",
message: "Create user with needs rotation?",
},
])
.then(({userID, password}) => {
return User.create(generateJWT(userID), password);
})
.then(({userID, password, needsRotation}) => User.create(generateJWT(userID), password, {needsRotation}))
.then((userInfo) => {
logWithMessage("User Created!", userInfo);
return Promise.resolve(false);
Expand Down
14 changes: 12 additions & 2 deletions ironnode.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ export interface DocumentAccessList {
users?: Array<{id: string}>;
groups?: Array<{id: string}>;
}

export interface UserCreateOptions {
needsRotation: boolean;
}
export interface DeviceCreateOptions {
deviceName: string;
}
Expand All @@ -24,6 +26,7 @@ export interface GroupCreateOptions {
groupID?: string;
groupName?: string;
addAsMember?: boolean;
needsRotation?: boolean;
}
export interface GroupUpdateOptions {
groupName: string | null;
Expand Down Expand Up @@ -93,6 +96,7 @@ export interface GroupListResponse {
export interface GroupDetailResponse extends GroupMetaResponse {
groupAdmins: string[];
groupMembers: string[];
needsRotation: boolean;
}
export interface GroupUserEditResponse {
succeeded: string[];
Expand Down Expand Up @@ -135,6 +139,7 @@ export interface Group {
get(groupID: string): Promise<GroupMetaResponse | GroupDetailResponse>;
create(options?: GroupCreateOptions): Promise<GroupDetailResponse>;
update(groupID: string, options: GroupUpdateOptions): Promise<GroupMetaResponse>;
rotatePrivateKey(groupID: string): Promise<{needsRotation: boolean}>;
deleteGroup(groupID: string): Promise<{id: string}>;
addAdmins(groupID: string, adminList: string[]): Promise<GroupUserEditResponse>;
removeAdmins(groupID: string, adminList: string[]): Promise<GroupUserEditResponse>;
Expand All @@ -146,12 +151,17 @@ export interface User {
getPublicKey(users: string | string[]): Promise<UserPublicKeyGetResponse>;
listDevices(): Promise<UserDeviceListResponse>;
deleteDevice(id?: number): Promise<{id: number}>;
rotateMasterKey(password: string): Promise<{needsRotation: boolean}>;
}

export interface SDK {
document: Document;
group: Group;
user: User;
userContext: {
userNeedsRotation: boolean;
groupsNeedingRotation: string[];
};
}

export class SDKError extends Error {
Expand Down Expand Up @@ -179,6 +189,6 @@ export interface DeviceDetails {

export namespace User {
export function verify(jwt: string): Promise<ApiUserResponse | undefined>;
export function create(jwt: string, password: string): Promise<ApiUserResponse>;
export function create(jwt: string, password: string, options?: UserCreateOptions): Promise<ApiUserResponse>;
export function generateDeviceKeys(jwt: string, password: string, options?: DeviceCreateOptions): Promise<DeviceDetails>;
}
2 changes: 1 addition & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ module.exports = {
branches: 95,
functions: 95,
lines: 95,
statements: -5,
statements: -10,
},
},
//Use ts-jest for all .ts files
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"license": "AGPL-3.0-only",
"types": "ironnode.d.ts",
"engines": {
"node": ">=8.0.0"
"node": ">=10.0.0"
},
"os": [
"darwin",
Expand All @@ -26,7 +26,7 @@
"lint": "tslint -p \"tsconfig.json\" -e \"**/tests/**\" \"src/**/*.ts\""
},
"dependencies": {
"@ironcorelabs/recrypt-node-binding": "0.6.1",
"@ironcorelabs/recrypt-node-binding": "0.7.0",
"futurejs": "2.1.1",
"node-fetch": "2.6.0"
},
Expand Down
6 changes: 6 additions & 0 deletions src/Constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ export const ErrorCodes = {
USER_DEVICE_KEY_GENERATION_FAILURE: 207,
USER_DEVICE_LIST_REQUEST_FAILURE: 208,
USER_DEVICE_DELETE_REQUEST_FAILURE: 209,
USER_UPDATE_KEY_REQUEST_FAILURE: 210,
USER_PRIVATE_KEY_ROTATION_FAILURE: 211,
DOCUMENT_LIST_REQUEST_FAILURE: 300,
DOCUMENT_GET_REQUEST_FAILURE: 301,
DOCUMENT_CREATE_REQUEST_FAILURE: 302,
Expand Down Expand Up @@ -66,5 +68,9 @@ export const ErrorCodes = {
GROUP_REMOVE_ADMINS_REQUEST_FAILURE: 412,
GROUP_UPDATE_REQUEST_FAILURE: 413,
GROUP_DELETE_REQUEST_FAILURE: 414,
GROUP_CREATE_WITH_MEMBERS_OR_ADMINS_FAILURE: 415,
GROUP_PRIVATE_KEY_ROTATION_FAILURE: 416,
GROUP_UPDATE_KEY_REQUEST_FAILURE: 417,
GROUP_ROTATE_PRIVATE_KEY_NOT_ADMIN_FAILURE: 418,
REQUEST_RATE_LIMITED: 500,
};
Loading

0 comments on commit 6911af9

Please sign in to comment.