Skip to content

Commit

Permalink
Group Management Page (#195)
Browse files Browse the repository at this point in the history
* add tab to header + hello world tab

* blah

* initial layout

* added node list and entity panel functionality

* more layout work and table styling

* move node selection logic out of entity info panel

* added types for responses, worked on set and domain selectors

* added search functionality

* refactor into shared ui

* add selector edit api request

* broke out changelog table

* rework types for changelog

* added default domain logic

* updated folder structure, added autocomplete styling

* worked on api filtering logic, built entity count display

* added link to explore page, group update status messages

* adjusted node icon placement

* added pagination controls

* added pagination functionality

* added table loading elements and styling

* formatter

* fixed scroll behavior of entity panel

* moved group management page layout into shared-ui

* refactor

* run license check

* add tests

* finished writing tests

* add license

* changes from feedback

* fixed breaking test

* incorporating additional feedback

* invalidate asset group members query on selector edit

* updated swagger definitions, query logic updates to account for api changes

* resolve merge conflicts

* add null check for system_tags property since it is now optional

* swapped GraphNodeTypes for EntityKinds

* resolving merge conflicts

* chore: add license snippet to utils.tsx

* chore: fix linting issue in types.ts

---------

Co-authored-by: Brandon Shearin <bshearin@specterops.io>
Co-authored-by: Eli K Miller <emiller@specterops.io>
  • Loading branch information
3 people authored Dec 15, 2023
1 parent 9d42508 commit f35ce41
Show file tree
Hide file tree
Showing 63 changed files with 1,996 additions and 194 deletions.
34 changes: 31 additions & 3 deletions cmd/api/src/docs/json/definitions/models.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,10 @@
"properties": {
"action": {
"type": "string",
"enum": ["add", "remove"]
"enum": [
"add",
"remove"
]
},
"selector_name": {
"type": "string"
Expand Down Expand Up @@ -562,7 +565,12 @@
},
"model.PaginatedResponse": {
"type": "object",
"required": ["count", "limit", "skip", "data"],
"required": [
"count",
"limit",
"skip",
"data"
],
"properties": {
"count": {
"type": "integer"
Expand Down Expand Up @@ -1105,5 +1113,25 @@
"format": "date-time"
}
}
},
"model.SearchResult": {
"type": "object",
"properties": {
"objectid": {
"type": "string"
},
"type": {
"type": "string"
},
"name": {
"type": "string"
},
"distinguishedname": {
"type": "string"
},
"system_tags": {
"type": "string"
}
}
}
}
}
98 changes: 77 additions & 21 deletions cmd/api/src/docs/json/definitions/v2.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@
},
"v2.RiskAcceptRequest": {
"type": "object",
"required": ["risk_type", "accepted"],
"required": [
"risk_type",
"accepted"
],
"properties": {
"risk_type": {
"type": "string"
Expand Down Expand Up @@ -57,9 +60,15 @@
"data": {
"type": "object",
"properties": {
"count": { "type": "integer" },
"limit": { "type": "integer" },
"skip": { "type": "integer" },
"count": {
"type": "integer"
},
"limit": {
"type": "integer"
},
"skip": {
"type": "integer"
},
"members": {
"type": "array",
"items": {
Expand Down Expand Up @@ -93,7 +102,10 @@
},
"v2.ClientCreateRequest": {
"type": "object",
"required": ["name", "type"],
"required": [
"name",
"type"
],
"properties": {
"domain_controller": {
"type": "string"
Expand All @@ -111,7 +123,9 @@
},
"v2.ClientUpdateRequest": {
"type": "object",
"required": ["name"],
"required": [
"name"
],
"properties": {
"name": {
"type": "string"
Expand Down Expand Up @@ -222,7 +236,10 @@
"status": {
"description": "Status code for complete (2) or failed (5)",
"type": "integer",
"enum": [2, 5],
"enum": [
2,
5
],
"required": true
},
"message": {
Expand Down Expand Up @@ -477,7 +494,10 @@
},
"v2.CreateUserRequest": {
"type": "object",
"required": ["email_address", "roles"],
"required": [
"email_address",
"roles"
],
"properties": {
"email_address": {
"type": "string",
Expand Down Expand Up @@ -618,7 +638,9 @@
},
"v2.ListAppConfigParametersResponse": {
"type": "object",
"required": ["data"],
"required": [
"data"
],
"properties": {
"data": {
"type": "object"
Expand All @@ -627,7 +649,9 @@
},
"v2.ListFlagsResponse": {
"type": "object",
"required": ["data"],
"required": [
"data"
],
"properties": {
"data": {
"type": "object"
Expand All @@ -636,7 +660,9 @@
},
"v2.ToggleFlagResponse": {
"type": "object",
"required": ["enabled"],
"required": [
"enabled"
],
"properties": {
"enabled": {
"type": "boolean"
Expand All @@ -645,7 +671,9 @@
},
"v2.ListSAMLSignOnEndpointsResponse": {
"type": "object",
"required": ["endpoints"],
"required": [
"endpoints"
],
"properties": {
"endpoints": {
"type": "boolean"
Expand All @@ -654,7 +682,9 @@
},
"v2.IDPValidationResponse": {
"type": "object",
"required": ["successful"],
"required": [
"successful"
],
"properties": {
"error_message": {
"type": "string"
Expand Down Expand Up @@ -697,7 +727,9 @@
},
"v2.MFAEnrollmentRequest": {
"type": "object",
"required": ["secret"],
"required": [
"secret"
],
"properties": {
"secret": {
"type": "string"
Expand All @@ -717,7 +749,9 @@
},
"v2.MFAStatusResponse": {
"type": "object",
"required": ["status"],
"required": [
"status"
],
"properties": {
"data": {
"type": "object",
Expand All @@ -731,7 +765,9 @@
},
"v2.MFAActivationRequest": {
"type": "object",
"required": ["otp"],
"required": [
"otp"
],
"properties": {
"otp": {
"type": "string"
Expand All @@ -740,7 +776,9 @@
},
"v2.LoginResponse": {
"type": "object",
"required": ["status"],
"required": [
"status"
],
"properties": {
"data": {
"type": "object",
Expand All @@ -760,7 +798,9 @@
},
"v2.ClientErrorRequest": {
"type": "object",
"required": ["task_error"],
"required": [
"task_error"
],
"properties": {
"task_error": {
"type": "string"
Expand All @@ -772,7 +812,9 @@
},
"v2.PagedNodeListEntry": {
"type": "object",
"required": ["object_id"],
"required": [
"object_id"
],
"properties": {
"object_id": {
"type": "string"
Expand Down Expand Up @@ -920,7 +962,10 @@
},
"v2.CreateSavedQueryRequest": {
"type": "object",
"required": ["name", "query"],
"required": [
"name",
"query"
],
"properties": {
"query": {
"type": "string"
Expand All @@ -929,5 +974,16 @@
"type": "string"
}
}
},
"v2.SearchResponse": {
"type": "object",
"properties": {
"data": {
"type": "array",
"items": {
"$ref": "#/definitions/model.SearchResult"
}
}
}
}
}
}
4 changes: 2 additions & 2 deletions cmd/api/src/docs/json/paths/v2/search.json
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@
"content": {
"application/json": {
"schema": {
"$ref": "#/definitions/api.ResponseWrapper"
"$ref": "#/definitions/v2.SearchResponse"
}
}
}
Expand All @@ -113,4 +113,4 @@
}
}
}
}
}
9 changes: 5 additions & 4 deletions cmd/api/src/model/model.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
// Copyright 2023 Specter Ops, Inc.
//
//
// Licensed under the Apache License, Version 2.0
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//
// http://www.apache.org/licenses/LICENSE-2.0
//
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
// SPDX-License-Identifier: Apache-2.0

package model
Expand Down Expand Up @@ -97,4 +97,5 @@ type SearchResult struct {
Type string `json:"type"`
Name string `json:"name"`
DistinguishedName string `json:"distinguishedname"`
SystemTags string `json:"system_tags"`
}
2 changes: 2 additions & 0 deletions cmd/api/src/queries/graph.go
Original file line number Diff line number Diff line change
Expand Up @@ -448,13 +448,15 @@ func nodeToSearchResult(node *graph.Node) model.SearchResult {
name, _ = node.Properties.GetOrDefault(common.Name.String(), "NO NAME").String()
objectID, _ = node.Properties.GetOrDefault(common.ObjectID.String(), "NO OBJECT ID").String()
distinguishedName, _ = node.Properties.GetOrDefault(ad.DistinguishedName.String(), "").String()
systemTags, _ = node.Properties.GetOrDefault(common.SystemTags.String(), "").String()
)

return model.SearchResult{
ObjectID: objectID,
Type: analysis.GetNodeKindDisplayLabel(node),
Name: name,
DistinguishedName: distinguishedName,
SystemTags: systemTags,
}
}

Expand Down
9 changes: 8 additions & 1 deletion cmd/ui/src/components/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
//
// SPDX-License-Identifier: Apache-2.0

import { faCog, faProjectDiagram } from '@fortawesome/free-solid-svg-icons';
import { faCog, faUsersRectangle, faProjectDiagram } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { AppBar, Box, IconButton, Link, Toolbar } from '@mui/material';
import makeStyles from '@mui/styles/makeStyles';
Expand Down Expand Up @@ -94,6 +94,13 @@ const Header: React.FC = () => {
onClick={() => navigate(routes.ROUTE_EXPLORE)}
data-testid='global_header_nav-explore'
/>
<MenuItem
title={'Group Management'}
icon={<FontAwesomeIcon icon={faUsersRectangle} size='sm' />}
active={location.pathname === routes.ROUTE_GROUP_MANAGEMENT}
onClick={() => navigate(routes.ROUTE_GROUP_MANAGEMENT)}
data-testid='global_header_nav-group-management'
/>
</div>

<div className={classes.title}></div>
Expand Down
1 change: 1 addition & 0 deletions cmd/ui/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,4 @@ export const MODERATE_THRESHOLD = 40;
export const ZERO_VALUE_API_DATE = '0001-01-01T00:00:00Z';

export const TIER_ZERO_TAG = 'admin_tier_0';
export const TIER_ZERO_LABEL = 'High Value';
1 change: 1 addition & 0 deletions cmd/ui/src/ducks/global/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

export const ROUTE_HOME = '/';
export const ROUTE_EXPLORE = '/explore';
export const ROUTE_GROUP_MANAGEMENT = '/group-management';
export const ROUTE_LOGIN = '/login';
export const ROUTE_CHANGE_PASSWORD = '/changepassword';
export const ROUTE_USER_DISABLED = '/user-disabled';
Expand Down
8 changes: 8 additions & 0 deletions cmd/ui/src/mocks/factories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,3 +80,11 @@ export const createMockSearchResult = (nodeType?: string) => {
]),
};
};

export const createMockDomain = () => ({
type: faker.helpers.arrayElement(['active-directory', 'azure']),
impactValue: faker.datatype.number({ min: 0, max: 100 }),
name: faker.internet.domainName(),
id: faker.datatype.uuid(),
collected: faker.datatype.boolean(),
});
Loading

0 comments on commit f35ce41

Please sign in to comment.