@j-schreiber/sf-cli-security-audit 0.18.1 → 0.18.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/lib/salesforce/repositories/connected-apps/oauth-tokens.js +3 -9
- package/lib/salesforce/repositories/connected-apps/oauth-tokens.js.map +1 -1
- package/lib/salesforce/repositories/connected-apps/queries.js +1 -3
- package/lib/salesforce/repositories/connected-apps/queries.js.map +1 -1
- package/lib/salesforce/repositories/users/queries.d.ts +11 -3
- package/lib/salesforce/repositories/users/queries.js +30 -5
- package/lib/salesforce/repositories/users/queries.js.map +1 -1
- package/lib/salesforce/repositories/users/users.d.ts +3 -4
- package/lib/salesforce/repositories/users/users.js +60 -57
- package/lib/salesforce/repositories/users/users.js.map +1 -1
- package/lib/salesforce/utils.d.ts +2 -0
- package/lib/salesforce/utils.js +11 -0
- package/lib/salesforce/utils.js.map +1 -0
- package/oclif.manifest.json +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -89,7 +89,7 @@ FLAG DESCRIPTIONS
|
|
|
89
89
|
essentially control, if a permission is allowed in a certain profile / permission set.
|
|
90
90
|
```
|
|
91
91
|
|
|
92
|
-
_See code: [src/commands/org/audit/init.ts](https://github.com/j-schreiber/js-sf-cli-security-audit/blob/v0.18.
|
|
92
|
+
_See code: [src/commands/org/audit/init.ts](https://github.com/j-schreiber/js-sf-cli-security-audit/blob/v0.18.2/src/commands/org/audit/init.ts)_
|
|
93
93
|
|
|
94
94
|
## `sf org audit run`
|
|
95
95
|
|
|
@@ -134,7 +134,7 @@ FLAG DESCRIPTIONS
|
|
|
134
134
|
never truncated.
|
|
135
135
|
```
|
|
136
136
|
|
|
137
|
-
_See code: [src/commands/org/audit/run.ts](https://github.com/j-schreiber/js-sf-cli-security-audit/blob/v0.18.
|
|
137
|
+
_See code: [src/commands/org/audit/run.ts](https://github.com/j-schreiber/js-sf-cli-security-audit/blob/v0.18.2/src/commands/org/audit/run.ts)_
|
|
138
138
|
|
|
139
139
|
## `sf org scan user-perms`
|
|
140
140
|
|
|
@@ -183,7 +183,7 @@ FLAG DESCRIPTIONS
|
|
|
183
183
|
userPermissions.yml.
|
|
184
184
|
```
|
|
185
185
|
|
|
186
|
-
_See code: [src/commands/org/scan/user-perms.ts](https://github.com/j-schreiber/js-sf-cli-security-audit/blob/v0.18.
|
|
186
|
+
_See code: [src/commands/org/scan/user-perms.ts](https://github.com/j-schreiber/js-sf-cli-security-audit/blob/v0.18.2/src/commands/org/scan/user-perms.ts)_
|
|
187
187
|
|
|
188
188
|
<!-- commandsstop -->
|
|
189
189
|
|
|
@@ -2,6 +2,7 @@ import { EventEmitter } from 'node:events';
|
|
|
2
2
|
import { Messages } from '@salesforce/core';
|
|
3
3
|
import { ResolveLifecycle } from '../../resolve-entity-lifecycle-bus.js';
|
|
4
4
|
import { envVars } from '../../../ux/environment.js';
|
|
5
|
+
import { chunkArray } from '../../utils.js';
|
|
5
6
|
import { ALL_EXISTING_USER_IDS, COUNT_TOKEN_QUERY, formatCountSoql, formatTokenSoql, OAUTH_TOKEN_QUERY, } from './queries.js';
|
|
6
7
|
Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
|
|
7
8
|
const messages = Messages.loadMessages('@j-schreiber/sf-cli-security-audit', 'metadataretrieve');
|
|
@@ -40,7 +41,7 @@ export default class OAuthTokens extends EventEmitter {
|
|
|
40
41
|
return allTokens;
|
|
41
42
|
}
|
|
42
43
|
async batchQueryTokens(allUserIds, options) {
|
|
43
|
-
const userIdChunks =
|
|
44
|
+
const userIdChunks = chunkArray(allUserIds, options.startingBatchSize);
|
|
44
45
|
const queryPromises = userIdChunks.map((idChunk) => this.fetchTokenChunk(idChunk, options));
|
|
45
46
|
const results = await Promise.all(queryPromises);
|
|
46
47
|
return results.flat();
|
|
@@ -49,7 +50,7 @@ export default class OAuthTokens extends EventEmitter {
|
|
|
49
50
|
const countResult = await this.con.query(formatCountSoql(userIds));
|
|
50
51
|
if (countResult.totalSize > options.totalSizeThreshold && options.startingBatchSize > 1) {
|
|
51
52
|
const reducedChunkSize = Math.floor(options.startingBatchSize / 2);
|
|
52
|
-
const subChunks =
|
|
53
|
+
const subChunks = chunkArray(userIds, reducedChunkSize);
|
|
53
54
|
const subResultProms = subChunks.map((chunk) => this.fetchTokenChunk(chunk, {
|
|
54
55
|
totalSizeThreshold: options.totalSizeThreshold,
|
|
55
56
|
startingBatchSize: reducedChunkSize,
|
|
@@ -75,11 +76,4 @@ export default class OAuthTokens extends EventEmitter {
|
|
|
75
76
|
return userResult.records.map((userRecord) => userRecord.Id);
|
|
76
77
|
}
|
|
77
78
|
}
|
|
78
|
-
function chunkUserIds(userIds, chunkSize) {
|
|
79
|
-
const chunks = [];
|
|
80
|
-
for (let i = 0; i < userIds.length; i += chunkSize) {
|
|
81
|
-
chunks.push(userIds.slice(i, i + chunkSize));
|
|
82
|
-
}
|
|
83
|
-
return chunks;
|
|
84
|
-
}
|
|
85
79
|
//# sourceMappingURL=oauth-tokens.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oauth-tokens.js","sourceRoot":"","sources":["../../../../src/salesforce/repositories/connected-apps/oauth-tokens.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAc,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,uCAAuC,CAAC;AACzE,OAAO,EAAE,OAAO,EAAE,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"oauth-tokens.js","sourceRoot":"","sources":["../../../../src/salesforce/repositories/connected-apps/oauth-tokens.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { joinToSoqlIN } from '../../utils.js';
|
|
1
2
|
export const CONNECTED_APPS_QUERY = 'SELECT Id,Name,OptionsAllowAdminApprovedUsersOnly FROM ConnectedApplication';
|
|
2
3
|
export const ALL_EXISTING_USER_IDS = 'SELECT Id FROM User';
|
|
3
4
|
export const EXTERNAL_CLIENT_APPS_QUERY = 'SELECT Id,MasterLabel,DeveloperName,DistributionState FROM ExternalClientApplication';
|
|
@@ -10,7 +11,4 @@ export function formatCountSoql(userIds) {
|
|
|
10
11
|
export function formatTokenSoql(userIds) {
|
|
11
12
|
return `${OAUTH_TOKEN_QUERY} WHERE UserId IN (${joinToSoqlIN(userIds)})`;
|
|
12
13
|
}
|
|
13
|
-
function joinToSoqlIN(userIds) {
|
|
14
|
-
return userIds.map((id) => `'${id}'`).join(',');
|
|
15
|
-
}
|
|
16
14
|
//# sourceMappingURL=queries.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"queries.js","sourceRoot":"","sources":["../../../../src/salesforce/repositories/connected-apps/queries.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,oBAAoB,GAAG,6EAA6E,CAAC;AAClH,MAAM,CAAC,MAAM,qBAAqB,GAAG,qBAAqB,CAAC;AAC3D,MAAM,CAAC,MAAM,0BAA0B,GACrC,sFAAsF,CAAC;AACzF,MAAM,CAAC,MAAM,0BAA0B,GACrC,2FAA2F,CAAC;AAC9F,MAAM,CAAC,MAAM,iBAAiB,GAAG,iFAAiF,CAAC;AACnH,MAAM,CAAC,MAAM,iBAAiB,GAAG,gCAAgC,CAAC;AAElE,MAAM,UAAU,eAAe,CAAC,OAAiB;IAC/C,OAAO,GAAG,iBAAiB,qBAAqB,YAAY,CAAC,OAAO,CAAC,GAAG,CAAC;AAC3E,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,OAAiB;IAC/C,OAAO,GAAG,iBAAiB,qBAAqB,YAAY,CAAC,OAAO,CAAC,GAAG,CAAC;AAC3E,CAAC
|
|
1
|
+
{"version":3,"file":"queries.js","sourceRoot":"","sources":["../../../../src/salesforce/repositories/connected-apps/queries.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE9C,MAAM,CAAC,MAAM,oBAAoB,GAAG,6EAA6E,CAAC;AAClH,MAAM,CAAC,MAAM,qBAAqB,GAAG,qBAAqB,CAAC;AAC3D,MAAM,CAAC,MAAM,0BAA0B,GACrC,sFAAsF,CAAC;AACzF,MAAM,CAAC,MAAM,0BAA0B,GACrC,2FAA2F,CAAC;AAC9F,MAAM,CAAC,MAAM,iBAAiB,GAAG,iFAAiF,CAAC;AACnH,MAAM,CAAC,MAAM,iBAAiB,GAAG,gCAAgC,CAAC;AAElE,MAAM,UAAU,eAAe,CAAC,OAAiB;IAC/C,OAAO,GAAG,iBAAiB,qBAAqB,YAAY,CAAC,OAAO,CAAC,GAAG,CAAC;AAC3E,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,OAAiB;IAC/C,OAAO,GAAG,iBAAiB,qBAAqB,YAAY,CAAC,OAAO,CAAC,GAAG,CAAC;AAC3E,CAAC"}
|
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
export declare const
|
|
2
|
-
export declare const ALL_USERS_DETAILS_QUERY = "SELECT Id,Username,Profile.Name,CreatedDate,LastLoginDate,IsActive FROM User WHERE UserType IN ('Standard')";
|
|
1
|
+
export declare const USERS_QUERY: string;
|
|
3
2
|
export declare const buildPermsetAssignmentsQuery: (userIds: string[]) => string;
|
|
4
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Builds aggregate query for login history. Query is expected to
|
|
5
|
+
* throw an exception, if too many rows are returned. The chunking
|
|
6
|
+
* logic depends on this exception, so LIMIT in query would BREAK this.
|
|
7
|
+
*
|
|
8
|
+
* @param userIds
|
|
9
|
+
* @param daysToAnalayse
|
|
10
|
+
* @returns
|
|
11
|
+
*/
|
|
12
|
+
export declare const buildScopedLoginHistoryQuery: (userIds: string[], daysToAnalayse?: number) => string;
|
|
@@ -1,10 +1,35 @@
|
|
|
1
|
-
|
|
2
|
-
export const
|
|
1
|
+
import { joinToSoqlIN } from '../../utils.js';
|
|
2
|
+
export const USERS_QUERY = buildUsersQuery();
|
|
3
3
|
// DYNAMIC QUERIES
|
|
4
4
|
export const buildPermsetAssignmentsQuery = (userIds) => `${USERS_PERMSET_ASSIGNMENTS_QUERY} AND AssigneeId IN (${userIds.map((userId) => `'${userId}'`).join(',')})`;
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Builds aggregate query for login history. Query is expected to
|
|
7
|
+
* throw an exception, if too many rows are returned. The chunking
|
|
8
|
+
* logic depends on this exception, so LIMIT in query would BREAK this.
|
|
9
|
+
*
|
|
10
|
+
* @param userIds
|
|
11
|
+
* @param daysToAnalayse
|
|
12
|
+
* @returns
|
|
13
|
+
*/
|
|
14
|
+
export const buildScopedLoginHistoryQuery = (userIds, daysToAnalayse) => {
|
|
15
|
+
const groupBy = 'LoginType,Application,UserId';
|
|
16
|
+
const where = daysToAnalayse
|
|
17
|
+
? `UserId IN (${joinToSoqlIN(userIds)}) AND LoginTime >= LAST_N_DAYS:${daysToAnalayse}`
|
|
18
|
+
: `UserId IN (${joinToSoqlIN(userIds)})`;
|
|
19
|
+
return `${USERS_LOGIN_HISTORY_QUERY} WHERE ${where} GROUP BY ${groupBy}`;
|
|
20
|
+
};
|
|
21
|
+
function buildUsersQuery() {
|
|
22
|
+
const fieldLiterals = [
|
|
23
|
+
'Id',
|
|
24
|
+
'Username',
|
|
25
|
+
'Profile.Name',
|
|
26
|
+
'CreatedDate',
|
|
27
|
+
'LastLoginDate',
|
|
28
|
+
'IsActive',
|
|
29
|
+
'(SELECT PermissionSet.Name FROM PermissionSetAssignments WHERE PermissionSet.IsOwnedByProfile = FALSE AND PermissionSet.NamespacePrefix = NULL)',
|
|
30
|
+
];
|
|
31
|
+
return `SELECT ${fieldLiterals.join(',')} FROM User WHERE UserType IN ('Standard')`;
|
|
32
|
+
}
|
|
8
33
|
// BASE QUERIES
|
|
9
34
|
const USERS_LOGIN_HISTORY_QUERY = 'SELECT LoginType,Application,UserId,COUNT(Id)LoginCount,MAX(LoginTime)LastLogin FROM LoginHistory';
|
|
10
35
|
const USERS_PERMSET_ASSIGNMENTS_QUERY = 'SELECT AssigneeId,PermissionSet.Name FROM PermissionSetAssignment WHERE PermissionSet.IsOwnedByProfile = FALSE AND PermissionSet.NamespacePrefix = NULL';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"queries.js","sourceRoot":"","sources":["../../../../src/salesforce/repositories/users/queries.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"queries.js","sourceRoot":"","sources":["../../../../src/salesforce/repositories/users/queries.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE9C,MAAM,CAAC,MAAM,WAAW,GAAG,eAAe,EAAE,CAAC;AAE7C,kBAAkB;AAClB,MAAM,CAAC,MAAM,4BAA4B,GAAG,CAAC,OAAiB,EAAU,EAAE,CACxE,GAAG,+BAA+B,uBAAuB,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,MAAM,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;AAE/G;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,4BAA4B,GAAG,CAAC,OAAiB,EAAE,cAAuB,EAAU,EAAE;IACjG,MAAM,OAAO,GAAG,8BAA8B,CAAC;IAC/C,MAAM,KAAK,GAAG,cAAc;QAC1B,CAAC,CAAC,cAAc,YAAY,CAAC,OAAO,CAAC,kCAAkC,cAAc,EAAE;QACvF,CAAC,CAAC,cAAc,YAAY,CAAC,OAAO,CAAC,GAAG,CAAC;IAC3C,OAAO,GAAG,yBAAyB,UAAU,KAAK,aAAa,OAAO,EAAE,CAAC;AAC3E,CAAC,CAAC;AAEF,SAAS,eAAe;IACtB,MAAM,aAAa,GAAG;QACpB,IAAI;QACJ,UAAU;QACV,cAAc;QACd,aAAa;QACb,eAAe;QACf,UAAU;QACV,iJAAiJ;KAClJ,CAAC;IACF,OAAO,UAAU,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC,2CAA2C,CAAC;AACtF,CAAC;AAED,eAAe;AACf,MAAM,yBAAyB,GAC7B,mGAAmG,CAAC;AACtG,MAAM,+BAA+B,GACnC,yJAAyJ,CAAC"}
|
|
@@ -4,6 +4,7 @@ export default class Users {
|
|
|
4
4
|
private readonly connection;
|
|
5
5
|
private readonly mdapiRepo;
|
|
6
6
|
private readonly usersMaxFetch;
|
|
7
|
+
private readonly startingBatchSize;
|
|
7
8
|
constructor(connection: Connection);
|
|
8
9
|
/**
|
|
9
10
|
* Resolve all users from the target connection. Options controls
|
|
@@ -15,10 +16,8 @@ export default class Users {
|
|
|
15
16
|
resolve(opts?: Partial<ResolveUsersOptions>): Promise<Map<string, User>>;
|
|
16
17
|
private fetchUsers;
|
|
17
18
|
private resolveLogins;
|
|
18
|
-
private
|
|
19
|
-
private
|
|
20
|
-
private resolvePermSetAssignments;
|
|
19
|
+
private fetchLoginAggregates;
|
|
20
|
+
private fetchLoginAggregateChunks;
|
|
21
21
|
private resolveProfiles;
|
|
22
22
|
private resolvePermissionSets;
|
|
23
|
-
private fetchAssignments;
|
|
24
23
|
}
|
|
@@ -1,20 +1,22 @@
|
|
|
1
1
|
import { Messages } from '@salesforce/core';
|
|
2
|
-
import { isNullish } from '../../../utils.js';
|
|
3
2
|
import MDAPI from '../../mdapi/mdapi.js';
|
|
4
3
|
import { envVars } from '../../../ux/environment.js';
|
|
5
4
|
import { ResolveLifecycle } from '../../resolve-entity-lifecycle-bus.js';
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
5
|
+
import { chunkArray } from '../../utils.js';
|
|
6
|
+
import { ResolveUsersOptionsSchema } from './user.types.js';
|
|
7
|
+
import { buildScopedLoginHistoryQuery, USERS_QUERY } from './queries.js';
|
|
8
8
|
Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
|
|
9
9
|
const messages = Messages.loadMessages('@j-schreiber/sf-cli-security-audit', 'metadataretrieve');
|
|
10
10
|
export default class Users {
|
|
11
11
|
connection;
|
|
12
12
|
mdapiRepo;
|
|
13
13
|
usersMaxFetch;
|
|
14
|
+
startingBatchSize;
|
|
14
15
|
constructor(connection) {
|
|
15
16
|
this.connection = connection;
|
|
16
17
|
this.mdapiRepo = MDAPI.create(this.connection);
|
|
17
18
|
this.usersMaxFetch = envVars.resolve('SAE_MAX_USERS_LIMIT') ?? 100_000;
|
|
19
|
+
this.startingBatchSize = 256;
|
|
18
20
|
}
|
|
19
21
|
/**
|
|
20
22
|
* Resolve all users from the target connection. Options controls
|
|
@@ -26,7 +28,7 @@ export default class Users {
|
|
|
26
28
|
async resolve(opts) {
|
|
27
29
|
const definitiveOpts = ResolveUsersOptionsSchema.parse(opts ?? {});
|
|
28
30
|
const result = new Map();
|
|
29
|
-
const usersOnOrg = await this.fetchUsers(definitiveOpts
|
|
31
|
+
const usersOnOrg = await this.fetchUsers(definitiveOpts);
|
|
30
32
|
for (const user of usersOnOrg) {
|
|
31
33
|
const usr = {
|
|
32
34
|
userId: user.Id,
|
|
@@ -36,31 +38,43 @@ export default class Users {
|
|
|
36
38
|
createdDate: Date.parse(user.CreatedDate),
|
|
37
39
|
profileName: user.Profile.Name,
|
|
38
40
|
};
|
|
41
|
+
if (definitiveOpts.withPermissions && user.PermissionSetAssignments) {
|
|
42
|
+
usr.assignments = user.PermissionSetAssignments.records.map((assignment) => ({
|
|
43
|
+
permissionSetIdentifier: assignment.PermissionSet.Name,
|
|
44
|
+
permissionSetSource: assignment.PermissionSetGroupId ? 'group' : 'direct',
|
|
45
|
+
...(assignment.PermissionSetGroup?.DeveloperName && {
|
|
46
|
+
groupName: assignment.PermissionSetGroup?.DeveloperName,
|
|
47
|
+
}),
|
|
48
|
+
}));
|
|
49
|
+
}
|
|
50
|
+
else if (definitiveOpts.withPermissions) {
|
|
51
|
+
usr.assignments = [];
|
|
52
|
+
}
|
|
39
53
|
result.set(user.Username, usr);
|
|
40
54
|
}
|
|
41
55
|
if (definitiveOpts.withLoginHistory) {
|
|
42
56
|
await this.resolveLogins(result, definitiveOpts.loginHistoryDaysToAnalyse);
|
|
43
57
|
}
|
|
44
|
-
if (definitiveOpts.
|
|
45
|
-
await this.
|
|
58
|
+
if (definitiveOpts.withPermissionsMetadata) {
|
|
59
|
+
await this.resolveProfiles(result);
|
|
60
|
+
await this.resolvePermissionSets(result);
|
|
46
61
|
}
|
|
47
62
|
return result;
|
|
48
63
|
}
|
|
49
64
|
// PRIVATE ZONE
|
|
50
|
-
async fetchUsers(
|
|
51
|
-
const usersOnOrg =
|
|
52
|
-
|
|
53
|
-
:
|
|
54
|
-
|
|
55
|
-
maxFetch: this.usersMaxFetch,
|
|
56
|
-
});
|
|
65
|
+
async fetchUsers(opts) {
|
|
66
|
+
const usersOnOrg = await this.connection.query(USERS_QUERY, {
|
|
67
|
+
autoFetch: true,
|
|
68
|
+
maxFetch: this.usersMaxFetch,
|
|
69
|
+
});
|
|
57
70
|
if (usersOnOrg.totalSize > this.usersMaxFetch) {
|
|
58
71
|
ResolveLifecycle.emitWarn(messages.getMessage('warning.TooManyActiveUsersIncreaseLimit', [usersOnOrg.totalSize, this.usersMaxFetch]));
|
|
59
72
|
}
|
|
60
|
-
return usersOnOrg.records;
|
|
73
|
+
return usersOnOrg.records.filter((user) => (opts.includeInactive ? true : user.IsActive));
|
|
61
74
|
}
|
|
62
75
|
async resolveLogins(users, daysToAnalyse) {
|
|
63
|
-
const
|
|
76
|
+
const loginAggregates = await this.fetchLoginAggregates(Array.from(users.values()).map((user) => user.userId), daysToAnalyse);
|
|
77
|
+
const userLogins = indexLoginData(loginAggregates.flat());
|
|
64
78
|
for (const user of users.values()) {
|
|
65
79
|
if (userLogins.has(user.userId)) {
|
|
66
80
|
user.logins = userLogins.get(user.userId);
|
|
@@ -70,37 +84,28 @@ export default class Users {
|
|
|
70
84
|
}
|
|
71
85
|
}
|
|
72
86
|
}
|
|
73
|
-
async
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
await this.resolveProfiles(users);
|
|
77
|
-
await this.resolvePermissionSets(users);
|
|
87
|
+
async fetchLoginAggregates(userIds, daysToAnalyse) {
|
|
88
|
+
try {
|
|
89
|
+
return await this.fetchLoginAggregateChunks(userIds, this.startingBatchSize, daysToAnalyse);
|
|
78
90
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
91
|
+
catch (error) {
|
|
92
|
+
if (typeof error === 'object' && error != null && 'errorCode' in error) {
|
|
93
|
+
// only split if it's aggregate queryMore() problem and we can still drill down
|
|
94
|
+
if (error.errorCode === 'EXCEEDED_ID_LIMIT' && userIds.length >= 2) {
|
|
95
|
+
// note for future me: This will fail, if a single user exists that has more than 2000 rows
|
|
96
|
+
// in this aggregate query. This would require more than 2000 combinations of "LoginType"
|
|
97
|
+
// and "Application" - time will tell if we need to add a dynamic LIMIT 2000 here with resolve warning.
|
|
98
|
+
return await this.fetchLoginAggregateChunks(userIds, Math.floor(userIds.length / 2), daysToAnalyse);
|
|
99
|
+
}
|
|
88
100
|
}
|
|
89
|
-
|
|
90
|
-
loginType: loginHistoryRow.LoginType,
|
|
91
|
-
loginCount: loginHistoryRow.LoginCount,
|
|
92
|
-
application: loginHistoryRow.Application,
|
|
93
|
-
lastLogin: Date.parse(loginHistoryRow.LastLogin),
|
|
94
|
-
});
|
|
101
|
+
throw error;
|
|
95
102
|
}
|
|
96
|
-
return partialUsers;
|
|
97
103
|
}
|
|
98
|
-
async
|
|
99
|
-
const
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
}
|
|
104
|
+
async fetchLoginAggregateChunks(userIds, chunkSize, daysToAnalyse) {
|
|
105
|
+
const initialIdChunks = chunkArray(userIds, chunkSize);
|
|
106
|
+
const loginAggregateProms = initialIdChunks.map((idChunk) => this.connection.query(buildScopedLoginHistoryQuery(idChunk, daysToAnalyse)));
|
|
107
|
+
const loginAggregates = await Promise.all(loginAggregateProms);
|
|
108
|
+
return loginAggregates.map((queryResult) => queryResult.records).flat();
|
|
104
109
|
}
|
|
105
110
|
async resolveProfiles(users) {
|
|
106
111
|
const profiles = await this.mdapiRepo.resolve('Profile', uniqueProfileNames(users.values()));
|
|
@@ -117,23 +122,21 @@ export default class Users {
|
|
|
117
122
|
}
|
|
118
123
|
}
|
|
119
124
|
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
}
|
|
127
|
-
assignments.get(assignment.AssigneeId).push({
|
|
128
|
-
permissionSetIdentifier: assignment.PermissionSet.Name,
|
|
129
|
-
permissionSetSource: assignment.PermissionSetGroupId ? 'group' : 'direct',
|
|
130
|
-
...(assignment.PermissionSetGroup?.DeveloperName && {
|
|
131
|
-
groupName: assignment.PermissionSetGroup?.DeveloperName,
|
|
132
|
-
}),
|
|
133
|
-
});
|
|
125
|
+
}
|
|
126
|
+
function indexLoginData(rawLogins) {
|
|
127
|
+
const loginData = new Map();
|
|
128
|
+
for (const loginHistoryRow of rawLogins) {
|
|
129
|
+
if (!loginData.has(loginHistoryRow.UserId)) {
|
|
130
|
+
loginData.set(loginHistoryRow.UserId, []);
|
|
134
131
|
}
|
|
135
|
-
|
|
132
|
+
loginData.get(loginHistoryRow.UserId).push({
|
|
133
|
+
loginType: loginHistoryRow.LoginType,
|
|
134
|
+
loginCount: loginHistoryRow.LoginCount,
|
|
135
|
+
application: loginHistoryRow.Application,
|
|
136
|
+
lastLogin: Date.parse(loginHistoryRow.LastLogin),
|
|
137
|
+
});
|
|
136
138
|
}
|
|
139
|
+
return loginData;
|
|
137
140
|
}
|
|
138
141
|
function uniquePermissionSetNames(users) {
|
|
139
142
|
const permSetNames = new Set();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"users.js","sourceRoot":"","sources":["../../../../src/salesforce/repositories/users/users.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAExD,OAAO,
|
|
1
|
+
{"version":3,"file":"users.js","sourceRoot":"","sources":["../../../../src/salesforce/repositories/users/users.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export function chunkArray(ids, chunkSize) {
|
|
2
|
+
const chunks = [];
|
|
3
|
+
for (let i = 0; i < ids.length; i += chunkSize) {
|
|
4
|
+
chunks.push(ids.slice(i, i + chunkSize));
|
|
5
|
+
}
|
|
6
|
+
return chunks;
|
|
7
|
+
}
|
|
8
|
+
export function joinToSoqlIN(ids) {
|
|
9
|
+
return ids.map((id) => `'${id}'`).join(',');
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/salesforce/utils.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,UAAU,CAAC,GAAa,EAAE,SAAiB;IACzD,MAAM,MAAM,GAAG,EAAE,CAAC;IAClB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,IAAI,SAAS,EAAE,CAAC;QAC/C,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC;IAC3C,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,GAAa;IACxC,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC9C,CAAC"}
|
package/oclif.manifest.json
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@j-schreiber/sf-cli-security-audit",
|
|
3
3
|
"description": "Salesforce CLI plugin to automate highly configurable security audits",
|
|
4
|
-
"version": "0.18.
|
|
4
|
+
"version": "0.18.2",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "git+https://github.com/j-schreiber/js-sf-cli-security-audit"
|