Skip to content

Commit

Permalink
Merge pull request #10 from agiledigital-labs/feature/IE-8
Browse files Browse the repository at this point in the history
Feature/ie 8
  • Loading branch information
dspasojevic authored Aug 29, 2022
2 parents c0a5498 + 4020669 commit b7825d1
Show file tree
Hide file tree
Showing 4 changed files with 169 additions and 54 deletions.
4 changes: 4 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ const rootCommand = yargs
describe: 'Okta URL for Organisation',
demandOption: true,
})
.group(
['client-id', 'private-key', 'organisation-url'],
'Okta connection settings:'
)
.help();

// Allows to configure handlers (any .js file in the scripts directory) with arguments (rootCommand in this case) at runtime.
Expand Down
80 changes: 80 additions & 0 deletions src/scripts/deactivate-user.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { Argv } from 'yargs';
import { RootCommand } from '..';

import {
oktaManageClient,
OktaConfiguration,
user,
User,
} from './services/user-service';

const deactivateUser = async (
oktaConfiguration: OktaConfiguration,
userId: string
): Promise<User> => {
const client = oktaManageClient(oktaConfiguration);

const oktaUser = await client.getUser(userId);

// eslint-disable-next-line functional/no-expression-statement
await oktaUser.deactivate();

const decativatedOktaUser = await client.getUser(userId);

return user(decativatedOktaUser);
};

export default (
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
rootCommand: RootCommand
): Argv<{
readonly clientId: string;
readonly privateKey: string;
readonly organisationUrl: string;
readonly userId: string;
}> =>
rootCommand.command(
'deactivate-user [user-id]',
'Deactivates the specified user',
// eslint-disable-next-line functional/no-return-void, @typescript-eslint/prefer-readonly-parameter-types
(yargs) => {
// eslint-disable-next-line functional/no-expression-statement
yargs.positional('user-id', {
describe: 'a unique identifier for the server',
type: 'string',
demandOption: true,
});
},
async (args: {
readonly clientId: string;
readonly privateKey: string;
readonly organisationUrl: string;
readonly userId: string;
}) => {
// eslint-disable-next-line functional/no-try-statement
try {
const user = await deactivateUser(
{
...args,
},
args.userId
);
// eslint-disable-next-line functional/no-expression-statement
console.info(user);
} catch (error: unknown) {
// eslint-disable-next-line functional/no-throw-statement
throw error instanceof Error
? new Error(
`Failed to deactivate user [${args.userId}] in [${args.organisationUrl}].`,
{
cause: error,
}
)
: new Error(
`Failed to deactivate user [${args.userId}] in [${
args.organisationUrl
}] because of [${JSON.stringify(error)}].`
);
}
}
);
68 changes: 14 additions & 54 deletions src/scripts/list-users.ts
Original file line number Diff line number Diff line change
@@ -1,55 +1,25 @@
import { Argv } from 'yargs';
import { RootCommand } from '..';
import * as okta from '@okta/okta-sdk-nodejs';

import { table } from 'table';

/**
* Subset of User information provided by Okta. See okta.User for further information on it's derived type.
* @see https://developer.okta.com/docs/reference/api/users/
*/
type User = {
/** The internal Okta identifier. */
readonly id: string;
/**
* User login string.
*/
readonly login: string;
/**
* User email adress.
*/
readonly email: string;
/**
* Name of the user.
*/
readonly name: string;
/**
* User status as a string.
*/
readonly status: string;
};
import {
oktaReadOnlyClient,
OktaConfiguration,
user,
User,
} from './services/user-service';

/**
* Gets a list of users given a set of arguments relating to the client's information.
*
* @param clientId clientId of the Okta API integration.
* @param privateKey privateKey of the Okta API integration (formatted as a string, parseable as a JWT JSON Object).
* @param organisationUrl organisation URL of the Okta tenancy from which users will be listed.
* @param oktaConfiguration configuration for the connection to the Okta API.
* @returns the list of users.
*
*/
const fetchUsers = async (
clientId: string,
privateKey: string,
organisationUrl: string
oktaConfiguration: OktaConfiguration
): Promise<readonly User[]> => {
const client = new okta.Client({
orgUrl: organisationUrl,
authorizationMode: 'PrivateKey',
clientId: clientId,
scopes: ['okta.users.read'],
privateKey: privateKey,
});
const client = oktaReadOnlyClient(oktaConfiguration);

// We need to populate users with all of the client data so it can be
// returned. Okta's listUsers() function returns a custom collection that
Expand All @@ -62,15 +32,9 @@ const fetchUsers = async (
await client
.listUsers()
// eslint-disable-next-line functional/no-return-void, @typescript-eslint/prefer-readonly-parameter-types
.each((user: okta.User) => {
.each((oktaUser) => {
// eslint-disable-next-line functional/immutable-data, functional/no-expression-statement
users.push({
id: user.id,
login: user.profile.login,
email: user.profile.email,
name: user.profile.displayName,
status: String(user.status),
});
users.push(user(oktaUser));
});

return users;
Expand Down Expand Up @@ -108,7 +72,6 @@ export default (
readonly clientId: string;
readonly privateKey: string;
readonly organisationUrl: string;
readonly long: boolean;
}> =>
rootCommand.command(
'list-users',
Expand All @@ -120,15 +83,12 @@ export default (
readonly clientId: string;
readonly privateKey: string;
readonly organisationUrl: string;
readonly long: boolean;
}) => {
// eslint-disable-next-line functional/no-try-statement
try {
const users = await fetchUsers(
args.clientId,
args.privateKey,
args.organisationUrl
);
const users = await fetchUsers({
...args,
});
const tabulated = usersTable(users);
// eslint-disable-next-line functional/no-expression-statement
console.info(tabulated);
Expand Down
71 changes: 71 additions & 0 deletions src/scripts/services/user-service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import * as okta from '@okta/okta-sdk-nodejs';

/**
* Subset of User information provided by Okta. See okta.User for further information on it's derived type.
* @see https://developer.okta.com/docs/reference/api/users/
*/
export type User = {
/** The internal Okta identifier. */
readonly id: string;
/**
* User login string.
*/
readonly login: string;
/**
* User email adress.
*/
readonly email: string;
/**
* Name of the user.
*/
readonly name: string;
/**
* User status as a string.
*/
readonly status: string;
};

// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
export const user = (oktaUser: okta.User) => ({
id: oktaUser.id,
login: oktaUser.profile.login,
email: oktaUser.profile.email,
name: oktaUser.profile.displayName,
status: String(oktaUser.status),
});

/**
* Configuration required to create an Okta client.
*/
export type OktaConfiguration = {
/** The identifier of the client application in Okta. */
readonly clientId: string;
/** JSON encoded private key for the application. */
readonly privateKey: string;
/** URL of the Okta organisation. */
readonly organisationUrl: string;
};

/**
* Creates a client that can read user information from Okta.
* @param oktaConfiguration configuration to use when construction the client.
* @returns the Okta client.
*/
export const oktaReadOnlyClient = (oktaConfiguration: OktaConfiguration) =>
new okta.Client({
...oktaConfiguration,
authorizationMode: 'PrivateKey',
scopes: ['okta.users.read'],
});

/**
* Creates a client that can read and manage user information in Okta.
* @param oktaConfiguration configuration to use when construction the client.
* @returns the Okta client.
*/
export const oktaManageClient = (oktaConfiguration: OktaConfiguration) =>
new okta.Client({
...oktaConfiguration,
authorizationMode: 'PrivateKey',
scopes: ['okta.users.manage'],
});

0 comments on commit b7825d1

Please sign in to comment.