Skip to content

Commit 775061f

Browse files
committed
Merge branch 'main' into fix_linter_issues_clone
2 parents a81a4d9 + 6c979e5 commit 775061f

File tree

13 files changed

+149
-67
lines changed

13 files changed

+149
-67
lines changed

.github/workflows/semgrep.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ on:
88
- .github/workflows/semgrep.yml
99
schedule:
1010
# random HH:MM to avoid a load spike on GitHub Actions at 00:00
11-
- cron: 12 15 * * *
11+
- cron: 0 7 * * *
1212
jobs:
1313
semgrep:
1414
name: Scan

.github/workflows/unit-tests.yml

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,6 @@ jobs:
1919
pip install -r requirements.txt
2020
- name: Ensure Config
2121
run: cp config.json.sample config.json
22-
- name: Ensure git pat token
23-
shell: bash
24-
env:
25-
CUSTOM_GITHUB_ACTIONS_PAT: ${{ secrets.CUSTOM_GITHUB_ACTIONS_PAT }}
26-
run: |
27-
sed -i "s|https://github.com/browserstack/enigma-access-modules.git|https://qwe:[email protected]/browserstack/enigma-access-modules.git|g" config.json
2822
- name: Setup access modules code base
2923
run: python -m scripts.clone_access_modules
3024
- name: Ensure access modules dependencies

.semgrepignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
*.md
2+
3+
nginx.conf.sample

Access/accessrequest_helper.py

Lines changed: 67 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -501,41 +501,48 @@ def _create_access(
501501
}
502502

503503
access = AccessV2.get(access_tag=access_tag, access_label=access_label)
504-
if access:
505-
if user_identity.access_mapping_exists(access):
504+
if not access:
505+
try:
506+
_create_access_mapping(
507+
access_tag=access_tag,
508+
access_label=access_label,
509+
user_identity=user_identity,
510+
request_id=request_id,
511+
access_reason=access_reason,
512+
)
513+
except Exception:
506514
return {
507-
"title": REQUEST_DUPLICATE_ERR_MSG["title"].format(
508-
access_tag=access.access_tag
509-
),
510-
"msg": REQUEST_DUPLICATE_ERR_MSG["msg"].format(
511-
access_label=json.dumps(access.access_label)
512-
),
515+
"title": REQUEST_DB_ERR_MSG["error_msg"],
516+
"msg": REQUEST_DB_ERR_MSG["msg"],
513517
}
518+
return {"title": "success", "msg": "success"}
514519

515-
try:
516-
_create_access_mapping(
517-
access=access,
518-
user_identity=user_identity,
519-
request_id=request_id,
520-
access_reason=access_reason,
521-
)
522-
except Exception:
520+
if user_identity.access_mapping_exists(access):
523521
return {
524-
"title": REQUEST_DB_ERR_MSG["error_msg"],
525-
"msg": REQUEST_DB_ERR_MSG["msg"],
522+
"title": REQUEST_DUPLICATE_ERR_MSG["title"].format(
523+
access_tag=access.access_tag
524+
),
525+
"msg": REQUEST_DUPLICATE_ERR_MSG["msg"].format(
526+
access_label=json.dumps(access.access_label)
527+
),
526528
}
529+
530+
user_identity.user_access_mapping.create(
531+
request_id=request_id,
532+
request_reason=access_reason,
533+
access=access,
534+
)
527535
return {"title": "success", "msg": "success"}
528536

529537

530538
@transaction.atomic
531539
def _create_access_mapping(
532-
user_identity, access, request_id, access_reason
540+
user_identity, access_tag, access_label, request_id, access_reason
533541
):
534542
""" Create AccessV2 and UserAccessMapping in db """
535-
if not access:
536-
access = AccessV2.objects.create(
537-
access_tag=access.access_tag, access_label=access.access_label
538-
)
543+
access = AccessV2.objects.create(
544+
access_tag=access_tag, access_label=access_label
545+
)
539546

540547
user_identity.user_access_mapping.create(
541548
request_id=request_id, request_reason=access_reason, access=access
@@ -722,6 +729,41 @@ def run_accept_request_task(
722729
return json_response
723730

724731

732+
def decline_group_membership(request, access_type, request_id, reason):
733+
""" Decline group membership """
734+
json_response = {}
735+
membership = MembershipV2.get_membership(request_id)
736+
737+
if not membership:
738+
json_response["error"] = INVALID_REQUEST_ERROR_MSG
739+
return json_response
740+
741+
if not is_request_valid(request_id, membership):
742+
json_response["error"] = USER_REQUEST_IN_PROCESS_ERR_MSG.format(
743+
request_id=request_id,
744+
)
745+
return json_response
746+
747+
membership.decline(reason, request.user.user)
748+
749+
notifications.send_mail_for_request_decline(
750+
request, "Membership Creation", request_id, reason, access_type
751+
)
752+
753+
logger.debug(
754+
USER_REQUEST_DECLINE_MSG.format(
755+
request_id=request_id,
756+
decline_reason=reason,
757+
)
758+
)
759+
json_response = {}
760+
json_response["msg"] = USER_REQUEST_DECLINE_MSG.format(
761+
request_id=request_id,
762+
decline_reason=reason,
763+
)
764+
return json_response
765+
766+
725767
def decline_individual_access(request, access_type, request_id, reason):
726768
""" Decline individual access """
727769
json_response = {}
@@ -730,6 +772,8 @@ def decline_individual_access(request, access_type, request_id, reason):
730772
if access_type == "declineNewGroup":
731773
access_mapping = GroupV2.get_pending_group(request_id)
732774
decline_new_group = True
775+
elif access_type == "declineMember":
776+
return decline_group_membership(request, access_type, request_id, reason)
733777
else:
734778
access_mapping = UserAccessMapping.get_access_request(request_id)
735779
access_type = access_mapping.access.access_tag

Access/models.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,15 @@ def approve_membership(membership_id, approver):
435435
membership = MembershipV2.objects.get(membership_id=membership_id)
436436
membership.approve(approver=approver)
437437

438+
def decline(self, reason, decliner):
439+
self.status = "Declined"
440+
self.decline_reason = reason
441+
self.approver = decliner
442+
self.save()
443+
444+
def is_already_processed(self):
445+
return self.status in ["Declined", "Approved", "Processing", "Revoked"]
446+
438447
def revoke_membership(self):
439448
self.status = "Revoked"
440449
self.save()
@@ -1200,7 +1209,10 @@ def deactivate(self):
12001209

12011210
def get_active_access_mapping(self):
12021211
return self.user_access_mapping.filter(
1203-
status__in=["Approved", "Pending"], access__access_tag=self.access_tag
1212+
status__in=["Approved", "Pending",
1213+
"SecondaryPending",
1214+
"GrantFailed"],
1215+
access__access_tag=self.access_tag
12041216
)
12051217

12061218
def get_all_granted_access_mappings(self):
@@ -1211,7 +1223,7 @@ def get_all_granted_access_mappings(self):
12111223

12121224
def get_all_non_approved_access_mappings(self):
12131225
return self.user_access_mapping.filter(
1214-
status__in=["approvefailed", "pending", "secondarypending", "grantfailed"]
1226+
status__in=["Pending", "SecondaryPending", "GrantFailed"]
12151227
)
12161228

12171229
def decline_all_non_approved_access_mappings(self, decline_reason):
@@ -1225,7 +1237,7 @@ def get_granted_access_mapping(self, access):
12251237

12261238
def get_non_approved_access_mapping(self, access):
12271239
return self.user_access_mapping.filter(
1228-
status__in=["approvefailed", "pending", "secondarypending", "grantfailed"],
1240+
status__in=["Pending", "SecondaryPending", "GrantFailed"],
12291241
access=access,
12301242
)
12311243

Access/views.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,7 @@ def _get_request_ids_for_bulk_processing(posted_request_ids, selector):
424424

425425

426426
@login_required
427+
@user_any_approver
427428
def decline_access(request, access_type, request_id):
428429
"""Decline an access request.
429430

Makefile

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@ setup_mounts:
44
@mkdir -p mounts/db
55
@mkdir -p mounts/mysql_db
66
@mkdir -p mounts/logs
7-
@mkdir -p mounts/modules
7+
@mkdir -p mounts/modules_web
8+
@mkdir -p mounts/modules_celery
89
@mkdir -p Access/access_modules
910
@chown $(APP_UID) mounts/db
1011
@chown $(APP_UID) mounts/mysql_db
1112
@chown $(APP_UID) mounts/logs
12-
@chown $(APP_UID) mounts/modules
13+
@chown $(APP_UID) mounts/modules_web
14+
@chown $(APP_UID) mounts/modules_celery
1315
@chown $(APP_UID) Access/access_modules
1416

1517
## make all : Run service, test and linter

README.md

Lines changed: 44 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
## Enigma Access Management
1+
# Enigma Access Management
22

33
![BrowserStack Logo](https://d98b8t1nnulk5.cloudfront.net/production/images/layout/logo-header.png?1469004780)
44

@@ -8,70 +8,97 @@
88
[![Security Scan](https://github.com/browserstack/enigma/actions/workflows/semgrep.yml/badge.svg)](https://github.com/browserstack/enigma/actions/workflows/semgrep.yml)
99

1010

11+
Manage access to tools through a single portal.
1112

12-
This tool consists of 2 different components: a central webserver and pluggable access modules.
13+
## What is Enigma?
14+
15+
Enigma is a web-based internal Access Management Tool that:
16+
* helps employees get access to various in-house and third-party systems and components like git repositories, cloud machines (via ssh), and dashboards.
17+
* facilitates book-keeping.
18+
* helps with compliance.
19+
* manages the inventory of all the tools in one place.
20+
21+
22+
This tool consists of 2 different components: a central web server and pluggable access modules.
1323

1424
This repo is the code-base for the central webserver.
1525
Refer to [this](https://github.com/browserstack/enigma-access-modules) for published access modules with this tool.
1626

1727
Refer to [this doc](/docs/%E2%80%9CHow-to%E2%80%9D%20guides/Adding%20Modules.md) on how to create custom access modules
1828

29+
### Problems Solved
30+
31+
Enigma access management tool was developed internally at BrowserStack to solve some of the problems we observed around access management for employees
32+
33+
* No single portal for an individual to view their access across tools
34+
* No single portal to manage access for employees across vendors
35+
* No central audit trail across tools for access granted and revoked for employees
36+
* Repetitive Ops for DevOps teams and tool owners for access grant and revoke requests
37+
* No standardized SOC2-compliant and GDPR-compliant method for managing individual and admin access for external tools
38+
* No simple consolidated pipeline to trigger offboarding an exit-ing employee to revoke all employee access across tools
39+
* No way for an individual to maintain separate identity per tool
40+
* Individuals might have multiple accounts for a single tool, there can be multiple org-wide domains for certain tools
41+
* No way to request, audit and track employee access outside of org-team hierarchy. Adhoc teams / groups support is needed.
42+
* employees might migrate across teams, sometimes access are needed for temporary projects which are not required for the whole team
43+
* No way of listing a bunch of access to grant to employees working on a project
44+
* In case an individual is added to a project, access request for all relavant tools should be raised with a single click (based on knowledge-base build on other individuals working on the project)
45+
1946
## Usage
2047

2148
The following steps are for hosting Enigma locally from published docker container images.
2249

2350
For development setup, follow this [doc](/docs/one-click-dev.md)
2451

25-
### Pre-requisistes
52+
#### Pre-requisites
2653

2754
You will need to have docker daemon running locally to run the published containers.
2855
If you don't have docker setup, follow the guidelines [here](https://docs.docker.com/get-docker/)
2956

30-
### Steps
57+
#### Steps
3158

3259
1. Ensure you have a valid `config.json` present locally.
3360

3461
The default [config.json.sample](https://github.com/browserstack/enigma/blob/main/config.json.sample) should be sufficient to start.
3562

36-
You can then add module-specific configuration for the modules you want integrated with Enigma.
63+
You can then add module-specific configuration for the modules you want to be integrated with Enigma.
3764
For detailed instructions on configuration, follow [this doc](/docs/Configuration%20Guide.md)
3865

39-
2. Run the enigma docker container by mounting the downloaded config to the container
66+
2. Run the Enigma docker container by mounting the downloaded config to the container
4067

4168
```bash
4269
docker run --rm --name enigma -p 8000:8000 -v "$(pwd)/config.json":/srv/code/dev/config.json browserstack/enigma:v1
4370
```
4471

45-
Ensure that you 8000 port is free to use, and ensure that path to config.json is correct.
72+
Ensure that the 8000 port is free to use, and ensure that path to config.json is correct.
4673

4774
That's it! Enigma should be running locally on port 8000
4875

4976

5077
For first time user sign-in, follow [this doc](/docs/%E2%80%9CHow-to%E2%80%9D%20guides/User%20Guides/First%20User%20Setup.md)
5178

5279

53-
## Contributing code
80+
## Contributing to this tool
5481

55-
- Python 3.11.0
56-
- pre-commit (see rules [below](#rules-enforced-by-the-pre-commit-hooks))
82+
- The codebase is tested for Python 3.11.0
83+
- Setup pre-commit hooks for development (see rules [below](#rules-enforced-by-the-pre-commit-hooks))
5784
- run: `npm install @commitlint/cli @commitlint/config-conventional`
5885
- run: `pip install pre-commit==2.21.0`
5986
- run: `pre-commit install --install-hooks --overwrite` in the base directory of this project
6087
- run: `pre-commit autoupdate`
6188
- run: `pre-commit run --all-files --show-diff-on-failure --color always`
6289

63-
### Commit Message Guideline
90+
#### Commit Message Guideline
6491

6592
Format: `<type>(<scope>): <subject>`
6693

6794
`<scope>` is optional
6895

69-
`Type` can be of following type:
96+
`Type` can be of the following type:
7097

7198
- `feat`: new feature for the user, not a new feature for build script
7299
- `fix`: bug fix for the user, not a fix to a build script
73100
- `docs`: changes to the documentation
74-
- `style`: formatting, missing semi colons, etc; no production code change
101+
- `style`: formatting, missing semi-colons, etc; no production code change
75102
- `refactor`: refactoring production code, eg. renaming a variable
76103
- `test`: adding missing tests, refactoring tests; no production code change
77104
- `chore`: updating grunt tasks etc; no production code change
@@ -81,20 +108,20 @@ Format: `<type>(<scope>): <subject>`
81108
- `perf`: a code change that improves performance
82109
- `revert`: revert to a commit
83110

84-
### Example
111+
#### Example
85112

86113
```
87114
feat: add hat wobble
88115
^--^ ^------------^
89116
| |
90-
| +-> Summary in present tense.
117+
| +-> Summary in the present tense.
91118
|
92119
+-------> Type: Feature addition
93120
94121
fix: fixes #xxx
95122
^--^ ^------------^
96123
| |
97-
| +-> Reference to the github issue.
124+
| +-> Reference to the GitHub issue.
98125
|
99126
+-------> Type: Bug fix
100127
```
@@ -108,4 +135,4 @@ References:
108135

109136

110137
## License
111-
See [LICENSE.md](.github/LICENSE.md)
138+
See [LICENSE.md](/LICENSE.md)
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Following are the steps to Setup Nginx in enigma
66
- Should have a host(domain) with ssl certificats that can be attached to nginx.
77
- Make sure the host points the public IP of the machine in which enigma is running on. (i.e., create an dns A record with host pointing to public IP of machine)
88
2. Create a folder in the root folder of enigma named `certs` which contains ssl certificate and key.
9-
3. Copy `nginx.conf.sample` file to `nginx.conf`
9+
3. Copy `nginx.conf.sample` file in this folder to `nginx.conf`
1010
3. Configure `nginx.conf` file.
1111
- update the hostname in the `nginx.conf` file
1212
```diff

0 commit comments

Comments
 (0)