Skip to content

Commit 9062d24

Browse files
feat: Add support for riskAssessment (#1217)
* feat: riskAssessments integration * feedback: nest new device settings under riskAssessment * feedbadck: null check for status code * feedback: remove useless catch * remove additional references to previous/legacy format * null checkery * shorthand * feedback: singular-ize riskAssessments * Update docs/resource-specific-documentation.md Co-authored-by: Kushal <[email protected]> * Update src/tools/constants.ts Co-authored-by: Kushal <[email protected]> * feedback: more renaming * fix: tests should reflect singular naming too * feat: update risk assessment schema structure - src/tools/auth0/handlers/riskAssessment.ts: nest 'enabled' under 'settings' object - src/tools/auth0/handlers/riskAssessment.ts: change required fields to include 'settings' * feat: update risk assessment structure and tests - src/context/directory/handlers/riskAssessment.ts: change ParsedRiskAssessment type to use RiskAssessmentSettings - src/context/yaml/handlers/riskAssessment.ts: change ParsedRiskAssessment type to use RiskAssessmentSettings - test/context/directory/riskAssessment.test.js: update settings structure in test cases - test/context/yaml/context.test.js: update settings structure in test cases - test/tools/auth0/handlers/riskAssessment.tests.js: update settings structure in handler tests - test/utils.js: update mock for risk assessments to reflect new structure * feat: update risk assessment configuration and examples - docs/resource-specific-documentation.md: enhance risk assessments section with detailed configuration options - examples/directory/risk-assessment/settings.json: add new settings.json file for risk assessment - examples/yaml/tenant.yaml: modify risk assessment structure to include settings * update e2e * feat: update risk assessment types and remove settings.json - src/context/directory/handlers/riskAssessment.ts: replace RiskAssessmentSettings with RiskAssessment - src/context/yaml/handlers/riskAssessment.ts: replace RiskAssessmentSettings with RiskAssessment - src/tools/auth0/handlers/riskAssessment.ts: rename RiskAssessmentSettings to RiskAssessment - src/types.ts: update riskAssessment type to use RiskAssessment instead of RiskAssessmentSettings - examples/directory/risk-assessments/settings.json: remove settings.json file * update e2e * update e2e * E2E add cross_origin_authentication to client configurations test-data * E2E updated * E2E:enable cross_origin_authentication for Deploy CLI client on test-data * e2e update * e2e update --------- Co-authored-by: Kushal <[email protected]>
1 parent 5160c90 commit 9062d24

30 files changed

+28707
-15609
lines changed

docs/resource-specific-documentation.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -707,3 +707,40 @@ clients:
707707
```
708708

709709
For more details, see the [Management API documentation](https://auth0.com/docs/api/management/v2).
710+
711+
## Risk Assessments
712+
713+
Risk assessments configuration allows you to enable or disable risk assessment features for your tenant.
714+
715+
- `settings.enabled`: toggles the feature true/flase (required)
716+
- `new_device.remember_for` (optional): days to remember devices
717+
718+
### YAML Example
719+
720+
```yaml
721+
# Contents of ./tenant.yaml
722+
riskAssessment:
723+
settings:
724+
enabled: true
725+
new_device:
726+
remember_for: 30
727+
```
728+
729+
### Directory Example
730+
731+
Folder: `./risk-assessment/`
732+
733+
File: `./risk-assessment/settings.json`
734+
735+
```json
736+
{
737+
"settings": {
738+
"enabled": true
739+
},
740+
"new_device": {
741+
"remember_for": 30
742+
}
743+
}
744+
```
745+
746+
For more details, see the [Management API documentation](https://auth0.com/docs/api/management/v2#!/Risk_Assessments/get_settings).
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"settings": {
3+
"enabled": false
4+
}
5+
}

examples/yaml/tenant.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,3 +451,9 @@ userAttributeProfiles:
451451
type: "email"
452452
required: true
453453

454+
riskAssessment:
455+
settings:
456+
enabled: false
457+
# new_device:
458+
# remember_for: 30
459+

src/context/directory/handlers/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import actions from './actions';
1818
import organizations from './organizations';
1919
import triggers from './triggers';
2020
import attackProtection from './attackProtection';
21+
import riskAssessment from './riskAssessment';
2122
import branding from './branding';
2223
import phoneProviders from './phoneProvider';
2324
import phoneTemplates from './phoneTemplates';
@@ -71,6 +72,7 @@ const directoryHandlers: {
7172
organizations,
7273
triggers,
7374
attackProtection,
75+
riskAssessment,
7476
branding,
7577
phoneProviders,
7678
phoneTemplates,
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import path from 'path';
2+
import fs from 'fs-extra';
3+
import { constants } from '../../../tools';
4+
import { dumpJSON, existsMustBeDir, isFile, loadJSON } from '../../../utils';
5+
import { DirectoryHandler } from '.';
6+
import DirectoryContext from '..';
7+
import { ParsedAsset } from '../../../types';
8+
import { RiskAssessment } from '../../../tools/auth0/handlers/riskAssessment';
9+
10+
type ParsedRiskAssessment = ParsedAsset<'riskAssessment', RiskAssessment>;
11+
12+
function parse(context: DirectoryContext): ParsedRiskAssessment {
13+
const riskAssessmentDirectory = path.join(context.filePath, constants.RISK_ASSESSMENT_DIRECTORY);
14+
const riskAssessmentFile = path.join(riskAssessmentDirectory, 'settings.json');
15+
16+
if (!existsMustBeDir(riskAssessmentDirectory)) {
17+
return { riskAssessment: null };
18+
}
19+
20+
if (!isFile(riskAssessmentFile)) {
21+
return { riskAssessment: null };
22+
}
23+
24+
const riskAssessment = loadJSON(riskAssessmentFile, {
25+
mappings: context.mappings,
26+
disableKeywordReplacement: context.disableKeywordReplacement,
27+
});
28+
29+
return {
30+
riskAssessment,
31+
};
32+
}
33+
34+
async function dump(context: DirectoryContext): Promise<void> {
35+
const { riskAssessment } = context.assets;
36+
37+
if (!riskAssessment) return;
38+
39+
const riskAssessmentDirectory = path.join(context.filePath, constants.RISK_ASSESSMENT_DIRECTORY);
40+
const riskAssessmentFile = path.join(riskAssessmentDirectory, 'settings.json');
41+
42+
fs.ensureDirSync(riskAssessmentDirectory);
43+
dumpJSON(riskAssessmentFile, riskAssessment);
44+
}
45+
46+
const riskAssessmentHandler: DirectoryHandler<ParsedRiskAssessment> = {
47+
parse,
48+
dump,
49+
};
50+
51+
export default riskAssessmentHandler;

src/context/yaml/handlers/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import organizations from './organizations';
1818
import actions from './actions';
1919
import triggers from './triggers';
2020
import attackProtection from './attackProtection';
21+
import riskAssessment from './riskAssessment';
2122
import branding from './branding';
2223
import phoneProviders from './phoneProvider';
2324
import phoneTemplates from './phoneTemplates';
@@ -69,6 +70,7 @@ const yamlHandlers: { [key in AssetTypes]: YAMLHandler<{ [key: string]: unknown
6970
organizations,
7071
triggers,
7172
attackProtection,
73+
riskAssessment,
7274
branding,
7375
phoneProviders,
7476
phoneTemplates,
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { YAMLHandler } from '.';
2+
import YAMLContext from '..';
3+
import { RiskAssessment } from '../../../tools/auth0/handlers/riskAssessment';
4+
import { ParsedAsset } from '../../../types';
5+
6+
type ParsedRiskAssessment = ParsedAsset<'riskAssessment', RiskAssessment>;
7+
8+
async function parse(context: YAMLContext): Promise<ParsedRiskAssessment> {
9+
const { riskAssessment } = context.assets;
10+
11+
if (!riskAssessment) return { riskAssessment: null };
12+
13+
return {
14+
riskAssessment,
15+
};
16+
}
17+
18+
async function dump(context: YAMLContext): Promise<ParsedRiskAssessment> {
19+
const { riskAssessment } = context.assets;
20+
21+
if (!riskAssessment) return { riskAssessment: null };
22+
23+
return {
24+
riskAssessment,
25+
};
26+
}
27+
28+
const riskAssessmentHandler: YAMLHandler<ParsedRiskAssessment> = {
29+
parse,
30+
dump,
31+
};
32+
33+
export default riskAssessmentHandler;

src/tools/auth0/handlers/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import * as actions from './actions';
2525
import * as triggers from './triggers';
2626
import * as organizations from './organizations';
2727
import * as attackProtection from './attackProtection';
28+
import * as riskAssessment from './riskAssessment';
2829
import * as logStreams from './logStreams';
2930
import * as customDomains from './customDomains';
3031
import * as themes from './themes';
@@ -69,6 +70,7 @@ const auth0ApiHandlers: { [key in AssetTypes]: any } = {
6970
triggers,
7071
organizations,
7172
attackProtection,
73+
riskAssessment,
7274
logStreams,
7375
customDomains,
7476
themes,
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import DefaultAPIHandler from './default';
2+
import { Assets } from '../../../types';
3+
import { Management, ManagementError } from 'auth0';
4+
5+
export const schema = {
6+
type: 'object',
7+
properties: {
8+
settings: {
9+
type: 'object',
10+
properties: {
11+
enabled: {
12+
type: 'boolean',
13+
description: 'Whether or not risk assessment is enabled.',
14+
},
15+
},
16+
required: ['enabled'],
17+
},
18+
new_device: {
19+
type: 'object',
20+
properties: {
21+
remember_for: {
22+
type: 'number',
23+
description: 'Length of time to remember devices for, in days.',
24+
},
25+
},
26+
required: ['remember_for'],
27+
},
28+
},
29+
required: ['settings'],
30+
};
31+
32+
export type RiskAssessment = {
33+
settings: Management.GetRiskAssessmentsSettingsResponseContent;
34+
new_device?: Management.GetRiskAssessmentsSettingsNewDeviceResponseContent;
35+
};
36+
37+
export default class RiskAssessmentHandler extends DefaultAPIHandler {
38+
existing: RiskAssessment;
39+
40+
constructor(config: DefaultAPIHandler) {
41+
super({
42+
...config,
43+
type: 'riskAssessment',
44+
});
45+
}
46+
47+
async getType(): Promise<RiskAssessment> {
48+
if (this.existing) {
49+
return this.existing;
50+
}
51+
52+
try {
53+
const [settings, newDeviceSettings] = await Promise.all([
54+
this.client.riskAssessments.settings.get(),
55+
this.client.riskAssessments.settings.newDevice.get().catch((err) => {
56+
if (err instanceof ManagementError && err?.statusCode === 404) {
57+
return { remember_for: 0 };
58+
}
59+
throw err;
60+
}),
61+
]);
62+
63+
const riskAssessment: RiskAssessment = {
64+
settings: settings,
65+
new_device: newDeviceSettings,
66+
...(newDeviceSettings.remember_for > 0 && {
67+
new_device: newDeviceSettings,
68+
}),
69+
};
70+
71+
this.existing = riskAssessment;
72+
return this.existing;
73+
} catch (err) {
74+
if (err instanceof ManagementError && err.statusCode === 404) {
75+
const riskAssessment: RiskAssessment = {
76+
settings: { enabled: false },
77+
};
78+
this.existing = riskAssessment;
79+
return this.existing;
80+
}
81+
throw err;
82+
}
83+
}
84+
85+
async processChanges(assets: Assets): Promise<void> {
86+
const { riskAssessment } = assets;
87+
88+
// Non-existing section means it doesn't need to be processed
89+
if (!riskAssessment) {
90+
return;
91+
}
92+
93+
const updates: Promise<unknown>[] = [];
94+
95+
// Update main settings (enabled flag)
96+
updates.push(this.client.riskAssessments.settings.update(riskAssessment?.settings));
97+
98+
// Update new device settings if provided
99+
if (riskAssessment.new_device) {
100+
updates.push(
101+
this.client.riskAssessments.settings.newDevice.update(riskAssessment.new_device)
102+
);
103+
}
104+
105+
await Promise.all(updates);
106+
this.updated += 1;
107+
this.didUpdate(riskAssessment);
108+
}
109+
}

src/tools/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ const constants = {
107107
CONNECTIONS_ID_NAME: 'id',
108108
ROLES_DIRECTORY: 'roles',
109109
ATTACK_PROTECTION_DIRECTORY: 'attack-protection',
110+
RISK_ASSESSMENT_DIRECTORY: 'risk-assessment',
110111
GUARDIAN_FACTORS: [
111112
'sms',
112113
'push-notification',

0 commit comments

Comments
 (0)