mirror of
https://github.com/dirkjanm/ROADtools
synced 2026-06-08 13:48:32 +00:00
roadrecon: support more policy controls in UI and policies plugin
This commit is contained in:
@@ -549,6 +549,7 @@
|
||||
<div *ngIf="user.userType == 'Guest'" class="mat-elevation-z4 pagedescription">
|
||||
Policy processing for guest users is currently not completely accurate. Policies scoped to guest types are not processed.
|
||||
</div>
|
||||
<div class="mat-elevation-z4 pagedescription">This table shows Conditional Access policies scoped to the current user. It only shows enabled policies, policies in report only mode or disabled policies are skipped.</div>
|
||||
<table mat-table [dataSource]="policies" class="full-width-table">
|
||||
|
||||
<!-- Name + state -->
|
||||
@@ -661,19 +662,83 @@
|
||||
</mat-chip-list>
|
||||
</div>
|
||||
|
||||
<div *ngIf="row.policyDetail.Conditions.SignInRisks?.Include" class="policy-chip-section">
|
||||
<span class="policy-section-label">Sign in risk</span>
|
||||
<mat-chip-list>
|
||||
<mat-chip *ngFor="let risk of row.policyDetail.Conditions.SignInRisks.Include[0].SignInRisks" disableRipple>{{risk}}</mat-chip>
|
||||
</mat-chip-list>
|
||||
</div>
|
||||
|
||||
<div *ngIf="row.policyDetail.Conditions.UserRisks?.Include" class="policy-chip-section">
|
||||
<span class="policy-section-label">User risk</span>
|
||||
<mat-chip-list>
|
||||
<mat-chip *ngFor="let risk of row.policyDetail.Conditions.UserRisks.Include[0].UserRisks" disableRipple>{{risk}}</mat-chip>
|
||||
</mat-chip-list>
|
||||
</div>
|
||||
|
||||
<div *ngIf="row.policyDetail.Controls?.length > 0" class="policy-chip-section">
|
||||
<span class="policy-section-label">Sign-in controls</span>
|
||||
<mat-chip-list>
|
||||
<mat-chip *ngFor="let control of row.policyDetail.Controls" disableRipple>
|
||||
{{control.Control ? control.Control.join(' or ') : ''}}{{control.AuthStrengthIds ? 'Auth strength: ' + control.AuthStrengthIds.join(', ') : ''}}
|
||||
</mat-chip>
|
||||
<ng-container *ngFor="let control of row.policyDetail.Controls">
|
||||
<mat-chip disableRipple *ngIf="control.Control">
|
||||
{{control.Control.join(' or ')}}
|
||||
</mat-chip>
|
||||
<ng-container *ngIf="control.AuthStrengthIds">
|
||||
<mat-chip disableRipple *ngFor="let guid of control.AuthStrengthIds"
|
||||
[matTooltip]="guid">
|
||||
Auth strength: {{utils.translateAuthStrength(guid)}}
|
||||
</mat-chip>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</mat-chip-list>
|
||||
</div>
|
||||
|
||||
<div *ngIf="row.policyDetail.ExtensibleControls?.length > 0" class="policy-chip-section">
|
||||
<span class="policy-section-label">Sign-in Extensible Controls</span>
|
||||
<mat-chip-list>
|
||||
<ng-container *ngFor="let control of row.policyDetail.ExtensibleControls">
|
||||
<mat-chip disableRipple *ngIf="control == 'MicrosoftManagedRemediation'">
|
||||
Microsoft Managed risk remediation
|
||||
</mat-chip>
|
||||
<mat-chip disableRipple *ngIf="control != 'MicrosoftManagedRemediation'">
|
||||
{{control}}
|
||||
</mat-chip>
|
||||
</ng-container>
|
||||
</mat-chip-list>
|
||||
</div>
|
||||
|
||||
<div *ngIf="row.policyDetail.SessionControls" class="policy-chip-section">
|
||||
<span class="policy-section-label">Session controls</span>
|
||||
<mat-chip-list>
|
||||
<mat-chip *ngFor="let control of row.policyDetail.SessionControls" disableRipple>{{control}}</mat-chip>
|
||||
<ng-container *ngFor="let control of row.policyDetail.SessionControls">
|
||||
|
||||
<!-- SignInFrequency with sub-type branching -->
|
||||
<ng-container *ngIf="control === 'SignInFrequency'">
|
||||
<mat-chip disableRipple *ngIf="!row.policyDetail.SignInFrequencyType">
|
||||
SignInFrequency (Unknown setting)
|
||||
</mat-chip>
|
||||
<mat-chip disableRipple *ngIf="row.policyDetail.SignInFrequencyType === 30">
|
||||
SignInFrequency (Every time)
|
||||
</mat-chip>
|
||||
<mat-chip disableRipple *ngIf="row.policyDetail.SignInFrequencyType === 10">
|
||||
SignInFrequency (Every {{row.policyDetail.SignInFrequencyTimeSpan}})
|
||||
</mat-chip>
|
||||
<mat-chip disableRipple *ngIf="row.policyDetail.SignInFrequencyType !== 30 && row.policyDetail.SignInFrequencyType !== 10 && row.policyDetail.SignInFrequencyType">
|
||||
SignInFrequency (Unknown SIF type {{row.policyDetail.SignInFrequencyType}})
|
||||
</mat-chip>
|
||||
</ng-container>
|
||||
|
||||
<!-- PersistentBrowserSessionMode -->
|
||||
<mat-chip disableRipple *ngIf="control === 'PersistentBrowserSessionMode'">
|
||||
PersistentBrowserSession: {{row.policyDetail.PersistentBrowserSessionMode}}
|
||||
</mat-chip>
|
||||
|
||||
<!-- Everything else -->
|
||||
<mat-chip disableRipple *ngIf="control !== 'SignInFrequency' && control !== 'PersistentBrowserSessionMode'">
|
||||
{{control}}
|
||||
</mat-chip>
|
||||
|
||||
</ng-container>
|
||||
</mat-chip-list>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -335,4 +335,13 @@ export class UtilitiesService {
|
||||
return dateString;
|
||||
}
|
||||
}
|
||||
|
||||
public translateAuthStrength(guid: string): string {
|
||||
const builtIn: { [key: string]: string } = {
|
||||
'00000000-0000-0000-0000-000000000002': 'Multi-factor authentication',
|
||||
'00000000-0000-0000-0000-000000000003': 'Passwordless MFA',
|
||||
'00000000-0000-0000-0000-000000000004': 'Phishing-resistant MFA'
|
||||
};
|
||||
return builtIn[guid] ?? `Unknown auth strength policy: ${guid} (probably custom)`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -337,36 +337,28 @@ class AccessPoliciesPlugin():
|
||||
return ot
|
||||
|
||||
def _parse_signinrisks(self, cond):
|
||||
try:
|
||||
srcond = cond['SignInRisks']
|
||||
except KeyError:
|
||||
return ''
|
||||
|
||||
ot = '<strong>Including</strong>: '
|
||||
for icrit in srcond['Include']:
|
||||
ot += ', '.join([escape(crit) for crit in icrit['SignInRisks']])
|
||||
|
||||
if 'Exclude' in srcond:
|
||||
ot += '\n<br /><strong>Excluding</strong>: '
|
||||
for icrit in srcond['Exclude']:
|
||||
ot += ', '.join([escape(crit) for crit in icrit['SignInRisks']])
|
||||
|
||||
return ot
|
||||
return self._parse_risks(cond, 'SignInRisks')
|
||||
|
||||
def _parse_agentrisks(self, cond):
|
||||
return self._parse_risks(cond, 'AgentIdRisks')
|
||||
|
||||
def _parse_userrisks(self, cond):
|
||||
return self._parse_risks(cond, 'UserRisks')
|
||||
|
||||
def _parse_risks(self, cond, riskskey):
|
||||
try:
|
||||
srcond = cond['AgentIdRisks']
|
||||
srcond = cond[riskskey]
|
||||
except KeyError:
|
||||
return ''
|
||||
|
||||
ot = '<strong>Including</strong>: '
|
||||
for icrit in srcond['Include']:
|
||||
ot += ', '.join([escape(crit) for crit in icrit['AgentIdRisks']])
|
||||
ot += ', '.join([escape(crit) for crit in icrit[riskskey]])
|
||||
|
||||
if 'Exclude' in srcond:
|
||||
ot += '\n<br /><strong>Excluding</strong>: '
|
||||
for icrit in srcond['Exclude']:
|
||||
ot += ', '.join([escape(crit) for crit in icrit['AgentIdRisks']])
|
||||
ot += ', '.join([escape(crit) for crit in icrit[riskskey]])
|
||||
|
||||
return ot
|
||||
|
||||
@@ -540,6 +532,17 @@ class AccessPoliciesPlugin():
|
||||
ucond.append(condition)
|
||||
return ', '.join(ucond)
|
||||
|
||||
def _parse_extensiblecontrols(self, cond):
|
||||
if not 'ExtensibleControls' in cond:
|
||||
return ''
|
||||
ucond = []
|
||||
for control in cond['ExtensibleControls']:
|
||||
if control == 'MicrosoftManagedRemediation':
|
||||
ucond.append('Microsoft Managed risk remediation')
|
||||
else:
|
||||
ucond.append(f'Unknown extensible control: {control}')
|
||||
return ', '.join(ucond)
|
||||
|
||||
def _parse_compressed_cidr(self,detail):
|
||||
if not 'CompressedCidrIpRanges' in detail:
|
||||
return ''
|
||||
@@ -598,8 +601,10 @@ class AccessPoliciesPlugin():
|
||||
out['locations'] = self.parse_wrapper(self._parse_locations, conditions, policy)
|
||||
out['clients'] = self.parse_wrapper(self._parse_clients, conditions, policy)
|
||||
out['signinrisks'] = self.parse_wrapper(self._parse_signinrisks, conditions, policy)
|
||||
out['userrisks'] = self.parse_wrapper(self._parse_userrisks, conditions, policy)
|
||||
out['agentrisks'] = self.parse_wrapper(self._parse_agentrisks, conditions, policy)
|
||||
out['sessioncontrols'] = self._parse_sessioncontrols(detail)
|
||||
out['extensiblecontrols'] = self._parse_extensiblecontrols(detail)
|
||||
out['devices'] = self.parse_wrapper(self._parse_devices, conditions, policy)
|
||||
|
||||
try:
|
||||
@@ -683,12 +688,16 @@ class AccessPoliciesPlugin():
|
||||
table += '<tr><td>At locations</td><td>{0}</td></tr>'.format(out['locations'])
|
||||
if out['signinrisks'] != '':
|
||||
table += '<tr><td>Sign-in risks</td><td>{0}</td></tr>'.format(out['signinrisks'])
|
||||
if out['userrisks'] != '':
|
||||
table += '<tr><td>User risks</td><td>{0}</td></tr>'.format(out['userrisks'])
|
||||
if out['agentrisks'] != '':
|
||||
table += '<tr><td>Agent risks</td><td>{0}</td></tr>'.format(out['agentrisks'])
|
||||
if out['authflows'] != '':
|
||||
table += '<tr><td>Authentication flows</td><td>{0}</td></tr>'.format(out['authflows'])
|
||||
if out['controls'] != '':
|
||||
table += '<tr><td>Controls</td><td>{0}</td></tr>'.format(out['controls'])
|
||||
if out['extensiblecontrols'] != '':
|
||||
table += '<tr><td>Extensible controls</td><td>{0}</td></tr>'.format(out['extensiblecontrols'])
|
||||
if out['sessioncontrols'] != '':
|
||||
table += '<tr><td>Session controls</td><td>{0}</td></tr>'.format(out['sessioncontrols'])
|
||||
table += '</tbody>'
|
||||
|
||||
@@ -131,7 +131,8 @@ class PoliciesPlugin():
|
||||
'Roles': self._get_role,
|
||||
'ServicePrincipals': self._get_serviceprincipal,
|
||||
'ServicePrincipalFilterRule': self._get_serviceprincipalrule,
|
||||
'GuestsOrExternalUsers': self._translate_guestsexternal
|
||||
'GuestsOrExternalUsers': self._translate_guestsexternal,
|
||||
'AgenticServicePrincipals': self._get_serviceprincipal
|
||||
}
|
||||
inscope_uids = []
|
||||
for ctype, clist in crit.items():
|
||||
|
||||
Reference in New Issue
Block a user