roadrecon: add group members in roles overview and add links to entra portal on users and groups

This commit is contained in:
Dirk-jan Mollema
2025-04-01 15:51:02 +02:00
parent 92b26bfcf3
commit d4ade9a88b
7 changed files with 77 additions and 19 deletions
@@ -5,5 +5,9 @@
<mat-slide-toggle [(ngModel)]="mfa" [checked]="mfa">
Show MFA columns (only accurate if you collected this data)
</mat-slide-toggle>
<br />
<mat-slide-toggle [(ngModel)]="portallinks" [checked]="portallinks">
Show management links to Entra portal on objects
</mat-slide-toggle>
</mat-card-content>
</mat-card>
@@ -9,6 +9,8 @@ import { LocalStorage } from 'ngx-webstorage';
export class ConfigComponent implements OnInit {
@LocalStorage()
public mfa;
@LocalStorage()
public portallinks;
constructor() { }
@@ -13,6 +13,7 @@
<tr *ngIf="group.isAssignableToRole != null"><th>Can be assigned to roles</th><td>{{ group.isAssignableToRole? 'Yes':'No' }}</td></tr>
<tr *ngIf="group.createdDateTime != null"><th>Created</th><td>{{ group.createdDateTime }}</td></tr>
<tr><th>Group source</th><td>{{ group.dirSyncEnabled? 'Synced with AD':'Cloud-only' }}</td></tr>
<tr *ngIf="showPortalLink"><th>Manage in Entra portal</th><td><a target="_blank" href="https://entra.microsoft.com/#view/Microsoft_AAD_IAM/GroupDetailsMenuBlade/~/Overview/groupId/{{ group.objectId }}"><mat-icon>open_in_new</mat-icon></a></td></tr>
</table>
</mat-card>
<mat-divider></mat-divider>
@@ -1,41 +1,46 @@
import { Component, OnInit, Inject, ViewChild } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { GroupsItem } from '../../aadobjects.service'
import { GroupsItem } from '../../aadobjects.service';
import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { MatSort } from '@angular/material/sort';
import { Location } from '@angular/common';
import { LocalStorageService } from 'ngx-webstorage';
@Component({
template: ''
})
export class GroupsdialogInitComponent implements OnInit {
user: GroupsItem;
myurl: string;
showPortalLink: boolean;
constructor(
private route: ActivatedRoute,
private router: Router,
public dialog: MatDialog,
private location: Location
private location: Location,
private localSt: LocalStorageService
) {
this.myurl = this.router.url;
this.showPortalLink = this.localSt.retrieve('portallinks');
}
ngOnInit() {
this.route.data
.subscribe((data: { user: GroupsItem }) => {
const dialogRef = this.dialog.open(GroupsdialogComponent, {
data: data.user
data: {
group: data.user,
showPortalLink: this.showPortalLink
}
});
dialogRef.afterClosed().subscribe(result => {
console.log(this.router.url);
console.log(this.myurl);
if(this.router.url == this.myurl){
this.location.back();
}
});
});
}
}
@Component({
@@ -49,11 +54,16 @@ export class GroupsdialogComponent {
public displayedColumnsServicePrincipal: string[] = ['displayName', 'servicePrincipalType']
public displayedColumnsOwners: string[] = ['displayName', 'userPrincipalName']
public displayedColumnsDevices: string[] = ['displayName', 'deviceModel', 'deviceOSType', 'deviceTrustType'];
public showPortalLink: boolean;
public group: GroupsItem;
@ViewChild(MatSort, { static: true }) sort: MatSort;
@ViewChild(MatSort, {static: true}) sort: MatSort;
constructor(
public dialogRef: MatDialogRef<GroupsdialogComponent>,
@Inject(MAT_DIALOG_DATA) public group: GroupsItem
) { }
@Inject(MAT_DIALOG_DATA) public data: { group: GroupsItem, showPortalLink: boolean }
) {
this.group = data.group;
this.showPortalLink = data.showPortalLink;
}
}
@@ -15,6 +15,7 @@
<tr><th>Account source</th><td>{{ user.dirSyncEnabled? 'Synced with AD':'Cloud-only' }}</td></tr>
<tr><th>Account type</th><td>{{ user.userType }}</td></tr>
<tr><th>Status</th><td>{{ user.accountEnabled? 'Enabled':'Disabled' }}</td></tr>
<tr *ngIf="showPortalLink"><th>Manage in Entra portal</th><td><a target="_blank" href="https://entra.microsoft.com/#blade/Microsoft_AAD_UsersAndTenants/UserProfileMenuBlade/userId/{{ user.objectId }}"><mat-icon>open_in_new</mat-icon></a></td></tr>
</table>
</mat-card>
<mat-divider></mat-divider>
@@ -4,26 +4,34 @@ import { UsersItem } from '../../aadobjects.service'
import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { MatSort } from '@angular/material/sort';
import { Location } from '@angular/common';
import { LocalStorageService } from 'ngx-webstorage';
@Component({
template: ''
})
export class UsersdialogInitComponent implements OnInit {
user: UsersItem;
myurl: string;
showPortalLink: boolean;
constructor(
private route: ActivatedRoute,
private router: Router,
public dialog: MatDialog,
private location: Location
private location: Location,
private localSt:LocalStorageService
) {
this.myurl = this.router.url;
this.showPortalLink = this.localSt.retrieve('portallinks');
}
ngOnInit() {
this.route.data
.subscribe((data: { user: UsersItem }) => {
const dialogRef = this.dialog.open(UsersdialogComponent, {
data: data.user
const dialogRef = this.dialog.open(UsersdialogComponent,{
data: {
user: data.user,
showPortalLink: this.showPortalLink
}
});
dialogRef.afterClosed().subscribe(result => {
if(this.router.url == this.myurl){
@@ -46,11 +54,15 @@ export class UsersdialogComponent {
public displayedColumnsServicePrincipals: string[] = ['displayName', 'publisherName', 'microsoftFirstParty', 'passwordCredentials', 'keyCredentials', 'appRoles', 'oauth2Permissions'];
public displayedColumnsDevices: string[] = ['displayName', 'deviceManufacturer', 'accountEnabled', 'deviceModel', 'deviceOSType', 'deviceOSVersion', 'deviceTrustType', 'isCompliant', 'isManaged', 'isRooted'];
public displayedColumnsApplications: string[] = ['displayName', 'passwordCredentials', 'keyCredentials', 'appRoles', 'oauth2Permissions'];
public showPortalLink: boolean;
public user: UsersItem;
@ViewChild(MatSort, {static: true}) sort: MatSort;
constructor(
public dialogRef: MatDialogRef<UsersdialogComponent>,
@Inject(MAT_DIALOG_DATA) public user: UsersItem
) { }
@Inject(MAT_DIALOG_DATA) public data: {user: UsersItem, showPortalLink: boolean}
) {
this.user = data.user;
this.showPortalLink = data.showPortalLink;
}
}
+30 -2
View File
@@ -507,9 +507,24 @@ def get_allroles():
'scopeNames': snames,
'scopeIds': sids
}
_, principal = resolve_objectid(assignment.principalId)
principalType, principal = resolve_objectid(assignment.principalId)
aobj['principal'] = principal
roleobj['assignments'].append(aobj)
if principalType == 'Group':
group = db.session.get(Group, assignment.principalId)
for member in group.memberUsers:
mp = users_schema.dump([member])[0]
mp['displayName'] = f"{principal['displayName']} member: {mp['displayName']}"
roleobj['assignments'].append({
'type': 'assignment',
'scope': assignment.resourceScopes,
'scopeTypes': stypes,
'scopeNames': snames,
'scopeIds': sids,
'principal': mp
})
for assignment in role.eligibleAssignments:
stypes, snames, sids = translate_rolescopes(assignment.resourceScopes)
aobj = {
@@ -519,9 +534,22 @@ def get_allroles():
'scopeNames': snames,
'scopeIds': sids
}
_, principal = resolve_objectid(assignment.principalId)
principalType, principal = resolve_objectid(assignment.principalId)
aobj['principal'] = principal
roleobj['assignments'].append(aobj)
if principalType == 'Group':
group = db.session.get(Group, assignment.principalId)
for member in group.memberUsers:
mp = users_schema.dump([member])[0]
mp['displayName'] = f"{principal['displayName']} member: {mp['displayName']}"
roleobj['assignments'].append({
'type': 'eligible',
'scope': assignment.resourceScopes,
'scopeTypes': stypes,
'scopeNames': snames,
'scopeIds': sids,
'principal': mp
})
allroles.append(roleobj)
return jsonify(allroles)