mirror of
https://github.com/bitwarden/directory-connector
synced 2025-12-20 18:23:13 +00:00
test command for cli
This commit is contained in:
@@ -12,18 +12,16 @@ import { I18nService } from 'jslib/abstractions/i18n.service';
|
|||||||
import { MessagingService } from 'jslib/abstractions/messaging.service';
|
import { MessagingService } from 'jslib/abstractions/messaging.service';
|
||||||
import { StateService } from 'jslib/abstractions/state.service';
|
import { StateService } from 'jslib/abstractions/state.service';
|
||||||
|
|
||||||
import { AzureDirectoryService } from '../../services/azure-directory.service';
|
|
||||||
import { GSuiteDirectoryService } from '../../services/gsuite-directory.service';
|
|
||||||
import { LdapDirectoryService } from '../../services/ldap-directory.service';
|
|
||||||
import { SyncService } from '../../services/sync.service';
|
import { SyncService } from '../../services/sync.service';
|
||||||
|
|
||||||
import { Entry } from '../../models/entry';
|
|
||||||
import { GroupEntry } from '../../models/groupEntry';
|
import { GroupEntry } from '../../models/groupEntry';
|
||||||
import { UserEntry } from '../../models/userEntry';
|
import { UserEntry } from '../../models/userEntry';
|
||||||
import { ConfigurationService } from '../../services/configuration.service';
|
import { ConfigurationService } from '../../services/configuration.service';
|
||||||
|
|
||||||
import { BroadcasterService } from 'jslib/angular/services/broadcaster.service';
|
import { BroadcasterService } from 'jslib/angular/services/broadcaster.service';
|
||||||
|
|
||||||
|
import { ConnectorUtils } from '../../utils';
|
||||||
|
|
||||||
const BroadcasterSubscriptionId = 'DashboardComponent';
|
const BroadcasterSubscriptionId = 'DashboardComponent';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@@ -105,63 +103,22 @@ export class DashboardComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
this.simPromise = new Promise(async (resolve, reject) => {
|
this.simPromise = new Promise(async (resolve, reject) => {
|
||||||
try {
|
try {
|
||||||
const result = await this.syncService.sync(!this.simSinceLast, true);
|
const result = await ConnectorUtils.simulate(this.syncService, this.i18nService, this.simSinceLast);
|
||||||
if (result[0] != null) {
|
this.simGroups = result.groups;
|
||||||
this.simGroups = result[0];
|
this.simUsers = result.users;
|
||||||
}
|
this.simEnabledUsers = result.enabledUsers;
|
||||||
if (result[1] != null) {
|
this.simDisabledUsers = result.disabledUsers;
|
||||||
this.simUsers = result[1];
|
this.simDeletedUsers = result.deletedUsers;
|
||||||
}
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.simGroups = null;
|
this.simGroups = null;
|
||||||
this.simUsers = null;
|
this.simUsers = null;
|
||||||
reject(e || this.i18nService.t('syncError'));
|
reject(e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const userMap = new Map<string, UserEntry>();
|
|
||||||
this.sort(this.simUsers);
|
|
||||||
for (const u of this.simUsers) {
|
|
||||||
userMap.set(u.externalId, u);
|
|
||||||
if (u.deleted) {
|
|
||||||
this.simDeletedUsers.push(u);
|
|
||||||
} else if (u.disabled) {
|
|
||||||
this.simDisabledUsers.push(u);
|
|
||||||
} else {
|
|
||||||
this.simEnabledUsers.push(u);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.sort(this.simGroups);
|
|
||||||
for (const g of this.simGroups) {
|
|
||||||
if (g.userMemberExternalIds == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const anyG = (g as any);
|
|
||||||
anyG.users = [];
|
|
||||||
for (const uid of g.userMemberExternalIds) {
|
|
||||||
if (userMap.has(uid)) {
|
|
||||||
anyG.users.push(userMap.get(uid));
|
|
||||||
} else {
|
|
||||||
anyG.users.push({ displayName: uid });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.sort(anyG.users);
|
|
||||||
}
|
|
||||||
|
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private sort(arr: Entry[]) {
|
|
||||||
arr.sort((a, b) => {
|
|
||||||
return this.i18nService.collator ? this.i18nService.collator.compare(a.displayName, b.displayName) :
|
|
||||||
a.displayName.localeCompare(b.displayName);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private async updateLastSync() {
|
private async updateLastSync() {
|
||||||
this.lastGroupSync = await this.configurationService.getLastGroupSyncDate();
|
this.lastGroupSync = await this.configurationService.getLastGroupSyncDate();
|
||||||
this.lastUserSync = await this.configurationService.getLastUserSyncDate();
|
this.lastUserSync = await this.configurationService.getLastUserSyncDate();
|
||||||
|
|||||||
24
src/commands/test.command.ts
Normal file
24
src/commands/test.command.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import * as program from 'commander';
|
||||||
|
|
||||||
|
import { I18nService } from 'jslib/abstractions/i18n.service';
|
||||||
|
|
||||||
|
import { SyncService } from '../services/sync.service';
|
||||||
|
|
||||||
|
import { ConnectorUtils } from '../utils';
|
||||||
|
|
||||||
|
import { Response } from 'jslib/cli/models/response';
|
||||||
|
import { TestResponse } from '../models/response/testResponse';
|
||||||
|
|
||||||
|
export class TestCommand {
|
||||||
|
constructor(private syncService: SyncService, private i18nService: I18nService) { }
|
||||||
|
|
||||||
|
async run(cmd: program.Command): Promise<Response> {
|
||||||
|
try {
|
||||||
|
const result = await ConnectorUtils.simulate(this.syncService, this.i18nService, cmd.last || false);
|
||||||
|
const res = new TestResponse(result);
|
||||||
|
return Response.success(res);
|
||||||
|
} catch (e) {
|
||||||
|
return Response.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
13
src/models/response/groupResponse.ts
Normal file
13
src/models/response/groupResponse.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { GroupEntry } from '../groupEntry';
|
||||||
|
|
||||||
|
export class GroupResponse {
|
||||||
|
externalId: string;
|
||||||
|
displayName: string;
|
||||||
|
userIds: string[];
|
||||||
|
|
||||||
|
constructor(g: GroupEntry) {
|
||||||
|
this.externalId = g.externalId;
|
||||||
|
this.displayName = g.displayName;
|
||||||
|
this.userIds = Array.from(g.userMemberExternalIds);
|
||||||
|
}
|
||||||
|
}
|
||||||
22
src/models/response/testResponse.ts
Normal file
22
src/models/response/testResponse.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import { GroupResponse } from './groupResponse';
|
||||||
|
import { UserResponse } from './userResponse';
|
||||||
|
|
||||||
|
import { SimResult } from '../simResult';
|
||||||
|
|
||||||
|
import { BaseResponse } from 'jslib/cli/models/response/baseResponse';
|
||||||
|
|
||||||
|
export class TestResponse implements BaseResponse {
|
||||||
|
object: string;
|
||||||
|
groups: GroupResponse[] = [];
|
||||||
|
enabledUsers: UserResponse[] = [];
|
||||||
|
disabledUsers: UserResponse[] = [];
|
||||||
|
deletedUsers: UserResponse[] = [];
|
||||||
|
|
||||||
|
constructor(result: SimResult) {
|
||||||
|
this.object = 'test';
|
||||||
|
this.groups = result.groups != null ? result.groups.map((g) => new GroupResponse(g)) : [];
|
||||||
|
this.enabledUsers = result.enabledUsers != null ? result.enabledUsers.map((u) => new UserResponse(u)) : [];
|
||||||
|
this.disabledUsers = result.disabledUsers != null ? result.disabledUsers.map((u) => new UserResponse(u)) : [];
|
||||||
|
this.deletedUsers = result.deletedUsers != null ? result.deletedUsers.map((u) => new UserResponse(u)) : [];
|
||||||
|
}
|
||||||
|
}
|
||||||
11
src/models/response/userResponse.ts
Normal file
11
src/models/response/userResponse.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { UserEntry } from '../userEntry';
|
||||||
|
|
||||||
|
export class UserResponse {
|
||||||
|
externalId: string;
|
||||||
|
displayName: string;
|
||||||
|
|
||||||
|
constructor(u: UserEntry) {
|
||||||
|
this.externalId = u.externalId;
|
||||||
|
this.displayName = u.displayName;
|
||||||
|
}
|
||||||
|
}
|
||||||
10
src/models/simResult.ts
Normal file
10
src/models/simResult.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import { GroupEntry } from './groupEntry';
|
||||||
|
import { UserEntry } from './userEntry';
|
||||||
|
|
||||||
|
export class SimResult {
|
||||||
|
groups: GroupEntry[] = [];
|
||||||
|
users: UserEntry[] = [];
|
||||||
|
enabledUsers: UserEntry[] = [];
|
||||||
|
disabledUsers: UserEntry[] = [];
|
||||||
|
deletedUsers: UserEntry[] = [];
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ import * as program from 'commander';
|
|||||||
import { Main } from './bwdc';
|
import { Main } from './bwdc';
|
||||||
|
|
||||||
import { ConfigCommand } from './commands/config.command';
|
import { ConfigCommand } from './commands/config.command';
|
||||||
|
import { TestCommand } from './commands/test.command';
|
||||||
|
|
||||||
import { UpdateCommand } from 'jslib/cli/commands/update.command';
|
import { UpdateCommand } from 'jslib/cli/commands/update.command';
|
||||||
|
|
||||||
@@ -57,10 +58,30 @@ export class Program {
|
|||||||
program.on('--help', () => {
|
program.on('--help', () => {
|
||||||
writeLn('\n Examples:');
|
writeLn('\n Examples:');
|
||||||
writeLn('');
|
writeLn('');
|
||||||
writeLn(' bwdc login');
|
writeLn(' bwdc test');
|
||||||
|
writeLn(' bwdc config server bitwarden.com');
|
||||||
|
writeLn(' bwdc update');
|
||||||
writeLn('', true);
|
writeLn('', true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
program
|
||||||
|
.command('test')
|
||||||
|
.description('Test a simulated sync.')
|
||||||
|
.option('-l, --last', 'Since the last successful sync.')
|
||||||
|
.on('--help', () => {
|
||||||
|
writeLn('\n Examples:');
|
||||||
|
writeLn('');
|
||||||
|
writeLn(' bwdc test');
|
||||||
|
writeLn(' bwdc test --last');
|
||||||
|
writeLn('', true);
|
||||||
|
})
|
||||||
|
.action(async (cmd) => {
|
||||||
|
await this.exitIfNotAuthed();
|
||||||
|
const command = new TestCommand(this.main.syncService, this.main.i18nService);
|
||||||
|
const response = await command.run(cmd);
|
||||||
|
this.processResponse(response);
|
||||||
|
});
|
||||||
|
|
||||||
program
|
program
|
||||||
.command('config <setting> <value>')
|
.command('config <setting> <value>')
|
||||||
.description('Configure CLI settings.')
|
.description('Configure CLI settings.')
|
||||||
@@ -187,14 +208,6 @@ export class Program {
|
|||||||
return out.trim() === '' ? null : out;
|
return out.trim() === '' ? null : out;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async exitIfLocked() {
|
|
||||||
await this.exitIfNotAuthed();
|
|
||||||
const hasKey = await this.main.cryptoService.hasKey();
|
|
||||||
if (!hasKey) {
|
|
||||||
this.processResponse(Response.error('Vault is locked.'), true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async exitIfAuthed() {
|
private async exitIfAuthed() {
|
||||||
const authed = await this.main.userService.isAuthenticated();
|
const authed = await this.main.userService.isAuthenticated();
|
||||||
if (authed) {
|
if (authed) {
|
||||||
|
|||||||
70
src/utils.ts
Normal file
70
src/utils.ts
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
import { I18nService } from 'jslib/abstractions/i18n.service';
|
||||||
|
|
||||||
|
import { SyncService } from './services/sync.service';
|
||||||
|
|
||||||
|
import { Entry } from './models/entry';
|
||||||
|
import { SimResult } from './models/simResult';
|
||||||
|
import { UserEntry } from './models/userEntry';
|
||||||
|
|
||||||
|
export class ConnectorUtils {
|
||||||
|
static async simulate(syncService: SyncService, i18nService: I18nService, sinceLast: boolean): Promise<SimResult> {
|
||||||
|
return new Promise(async (resolve, reject) => {
|
||||||
|
const simResult = new SimResult();
|
||||||
|
try {
|
||||||
|
const result = await syncService.sync(!sinceLast, true);
|
||||||
|
if (result[0] != null) {
|
||||||
|
simResult.groups = result[0];
|
||||||
|
}
|
||||||
|
if (result[1] != null) {
|
||||||
|
simResult.users = result[1];
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
simResult.groups = null;
|
||||||
|
simResult.users = null;
|
||||||
|
reject(e || i18nService.t('syncError'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const userMap = new Map<string, UserEntry>();
|
||||||
|
this.sortEntries(simResult.users, i18nService);
|
||||||
|
for (const u of simResult.users) {
|
||||||
|
userMap.set(u.externalId, u);
|
||||||
|
if (u.deleted) {
|
||||||
|
simResult.deletedUsers.push(u);
|
||||||
|
} else if (u.disabled) {
|
||||||
|
simResult.disabledUsers.push(u);
|
||||||
|
} else {
|
||||||
|
simResult.enabledUsers.push(u);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.sortEntries(simResult.groups, i18nService);
|
||||||
|
for (const g of simResult.groups) {
|
||||||
|
if (g.userMemberExternalIds == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const anyG = (g as any);
|
||||||
|
anyG.users = [];
|
||||||
|
for (const uid of g.userMemberExternalIds) {
|
||||||
|
if (userMap.has(uid)) {
|
||||||
|
anyG.users.push(userMap.get(uid));
|
||||||
|
} else {
|
||||||
|
anyG.users.push({ displayName: uid });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.sortEntries(anyG.users, i18nService);
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(simResult);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static sortEntries(arr: Entry[], i18nService: I18nService) {
|
||||||
|
arr.sort((a, b) => {
|
||||||
|
return i18nService.collator ? i18nService.collator.compare(a.displayName, b.displayName) :
|
||||||
|
a.displayName.localeCompare(b.displayName);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user