Skip to content

Commit ce76cfd

Browse files
awolfdenAdam WolfmanAdam Wolfmanadamzinder
authored
Redesign Audit Logs Example Application (#31)
* Implement new design skeleton * Update content navigation logic * Update tab content * Update to use pythonic operators * updates requirements * Update all WorkOS SDK versions in requirements * Add org selection, table ui, and pagination example to login page * Update readme to align with new UI * Update readme to align with new UI * Remove print statements * Refactor app navigation * Remove unused template * Add pagination table to dsync app home template Co-authored-by: Adam Wolfman <[email protected]> Co-authored-by: Adam Wolfman <[email protected]> Co-authored-by: Adam <[email protected]>
1 parent 8746abb commit ce76cfd

File tree

18 files changed

+1086
-724
lines changed

18 files changed

+1086
-724
lines changed

python-flask-admin-portal-example/requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,5 @@ MarkupSafe==2.0.1
99
requests==2.26.0
1010
urllib3==1.26.7
1111
Werkzeug==2.0.1
12-
workos>=1.20.2
12+
workos>=1.21.0
1313
python-dotenv

python-flask-audit-logs-example/README.md

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ An example Flask application demonstrating how to use the [WorkOS Python SDK](ht
7575
```bash
7676
(env) $ echo $WORKOS_API_KEY
7777
(env) $ echo $WORKOS_CLIENT_ID
78+
(env) $ echo $APP_SECRET_KEY
7879
```
7980

8081
9. The final setup step is to start the server.
@@ -106,19 +107,16 @@ You can stop the local Flask server for now by entering `CTRL + c` on the comman
106107

107108
## Audit Logs Setup with WorkOS
108109

109-
10. Follow the [Audit Logs configuration steps](https://workos.com/docs/audit-logs/emit-an-audit-log-event/sign-in-to-your-workos-dashboard-account-and-configure-audit-log-event-schemas) to set up the following 5 events that are sent with this example:
110+
10. Follow the [Audit Logs configuration steps](https://workos.com/docs/audit-logs/emit-an-audit-log-event/sign-in-to-your-workos-dashboard-account-and-configure-audit-log-event-schemas) to set up the following 2 events that are sent with this example:
110111

111-
Action title: "user.signed_in" | Target type: "team"
112-
Action title: "user.logged_out" | Target type: "team"
113112
Action title: "user.organization_set" | Target type: "team"
114113
Action title: "user.organization_deleted" | Target type: "team"
115-
Action title: "user.connection_deleted" | Target type: "team"
116114

117-
11. Next, take note of the Organization ID for the Org which you will be sending the Audit Log events for. This ID gets entered into the splash page of the example application.
115+
11. Configure the Admin Portal Redirect URI.
118116

119-
12. Once you enter the Organization ID and submit it, you will be brought to the page where you'll be able to send the audit log events that were just configured. You'll also notice that the action of setting the Organization triggered an Audit Log already. Click the buttons to send the respective events.
117+
Navigate to the Configuration tab in your WorkOS Dshboard. From there click the Admin Portal tab. Click the Edit Admin Portal Redirect Links button and add "http://localhost:5000" to the "When clicking the back navigation, return users to:" input, then click Save Redirect Links.
120118

121-
13. To obtain a CSV of the Audit Log events that were sent for the last 30 days, click the "Export Events" button. This will bring you to a new page where you can download the events. Downloading the events is a 2 step process. First you need to create the report by clicking the "Generate CSV" button. Then click the "Access CSV" button to download a CSV of the Audit Log events for the selected Organization for the past 30 days.
119+
12. To obtain a CSV of the Audit Log events that were sent for the last 30 days, click the "Export Events" tab. This will bring you to a new page where you can download the events. Downloading the events is a 2 step process. First you need to create the report by clicking the "Generate CSV" button. Then click the "Access CSV" button to download a CSV of the Audit Log events for the selected Organization for the past 30 days. You may also adjust the time range using the form inputs.
122120

123121
## Need help?
124122

python-flask-audit-logs-example/app.py

Lines changed: 92 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,20 @@
55
import workos
66
from datetime import datetime, timedelta
77
from audit_log_events import (
8-
user_signed_in,
9-
user_logged_out,
10-
user_connection_deleted,
11-
user_organization_deleted,
128
user_organization_set,
139
)
10+
from flask_lucide import Lucide
11+
1412

1513
# Flask Setup
1614
DEBUG = False
1715
app = Flask(__name__)
1816
app.secret_key = os.getenv("APP_SECRET_KEY")
1917

20-
# WorkOS Setup
18+
lucide = Lucide(app)
19+
2120

21+
# WorkOS Setup
2222
workos.api_key = os.getenv("WORKOS_API_KEY")
2323
workos.project_id = os.getenv("WORKOS_CLIENT_ID")
2424
workos.base_api_url = "http://localhost:7000/" if DEBUG else workos.base_api_url
@@ -33,19 +33,39 @@ def to_pretty_json(value):
3333

3434
@app.route("/", methods=["POST", "GET"])
3535
def index():
36-
try:
36+
try:
37+
link = workos.client.portal.generate_link(
38+
organization=session["organization_id"], intent="audit_logs"
39+
)
40+
today = datetime.today()
41+
last_month = today - timedelta(days=30)
3742
return render_template(
3843
"send_events.html",
44+
link=link["link"],
3945
organization_id=session["organization_id"],
4046
org_name=session["organization_name"],
47+
last_month_iso=last_month.isoformat(),
48+
today_iso=today.isoformat(),
4149
)
4250
except KeyError:
43-
return render_template("login.html")
51+
before = request.args.get("before")
52+
after = request.args.get("after")
53+
organizations = workos.client.organizations.list_organizations(
54+
before=before, after=after, limit=5, order=None
55+
)
56+
before = organizations["listMetadata"]["before"]
57+
after = organizations["listMetadata"]["after"]
58+
return render_template(
59+
"login.html",
60+
organizations=organizations["data"],
61+
before=before,
62+
after=after
63+
)
4464

4565

4666
@app.route("/set_org", methods=["POST", "GET"])
4767
def set_org():
48-
organization_id = request.form["org"]
68+
organization_id = request.args.get("id")
4969
session["organization_id"] = organization_id
5070
organization_set = workos.client.audit_logs.create_event(
5171
organization_id, user_organization_set
@@ -58,59 +78,101 @@ def set_org():
5878

5979
@app.route("/send_event", methods=["POST", "GET"])
6080
def send_event():
61-
event_type = request.form["event"]
81+
event_version, actor_name, actor_type, target_name, target_type = (
82+
request.form["event-version"],
83+
request.form["actor-name"],
84+
request.form["actor-type"],
85+
request.form["target-name"],
86+
request.form["target-type"],
87+
)
6288
organization_id = session["organization_id"]
63-
events = [
64-
user_signed_in,
65-
user_logged_out,
66-
user_organization_deleted,
67-
user_connection_deleted,
68-
]
69-
event = events[int(event_type)]
89+
90+
event = {
91+
"action": "user.organization_deleted",
92+
"version": int(event_version),
93+
"occurred_at": datetime.now().isoformat(),
94+
"actor": {
95+
"type": actor_type,
96+
"name": actor_name,
97+
"id": "user_01GBNJC3MX9ZZJW1FSTF4C5938",
98+
},
99+
"targets": [
100+
{
101+
"type": target_type,
102+
"name": target_name,
103+
"id": "team_01GBNJD4MKHVKJGEWK42JNMBGS",
104+
},
105+
],
106+
"context": {
107+
"location": "123.123.123.123",
108+
"user_agent": "Chrome/104.0.0.0",
109+
},
110+
}
70111
organization_set = workos.client.audit_logs.create_event(organization_id, event)
71112
return redirect("/")
72113

73114

74115
@app.route("/export_events", methods=["POST", "GET"])
75116
def export_events():
117+
today = datetime.today()
118+
last_month = today - timedelta(days=30)
76119
organization_id = session["organization_id"]
77120
return render_template(
78-
"export_events.html",
121+
"send_events.html",
79122
organization_id=organization_id,
80123
org_name=session["organization_name"],
124+
last_month_iso=last_month.isoformat(),
125+
today_iso=today.isoformat(),
81126
)
82127

83128

84129
@app.route("/get_events", methods=["POST", "GET"])
85130
def get_events():
86131
organization_id = session["organization_id"]
87132
event_type = request.form["event"]
88-
today = datetime.today()
89-
last_month = today - timedelta(days=30)
90-
last_month_iso = last_month.isoformat()
91-
today_iso = today.isoformat()
92133

93134
if event_type == "generate_csv":
94-
create_export_response = workos.client.audit_logs.create_export(
95-
organization=organization_id,
96-
range_start=last_month_iso,
97-
range_end=today_iso,
98-
)
99-
session["export_id"] = create_export_response.to_dict()["id"]
100-
135+
if request.form["filter-actions"] != "":
136+
actions = request.form["filter-actions"]
137+
else:
138+
actions = None
139+
if request.form["filter-actors"] != "":
140+
actors = request.form["filter-actors"]
141+
else:
142+
actors = None
143+
if request.form["filter-targets"] != "":
144+
targets = request.form["filter-targets"]
145+
else:
146+
targets = None
147+
148+
try:
149+
150+
create_export_response = workos.client.audit_logs.create_export(
151+
organization=organization_id,
152+
range_start=request.form["range-start"],
153+
range_end=request.form["range-end"],
154+
actions=actions,
155+
actors=actors,
156+
targets=targets,
157+
)
158+
session["export_id"] = create_export_response.to_dict()["id"]
159+
160+
return redirect("export_events")
161+
except Exception as e:
162+
print(str(e))
163+
return redirect("/")
101164
if event_type == "access_csv":
102165
export_id = session["export_id"]
103166
fetch_export_response = workos.client.audit_logs.get_export(export_id)
104167
return redirect(fetch_export_response.to_dict()["url"])
105168

106-
return redirect("export_events")
107169

108170
@app.route("/events", methods=["GET"])
109171
def events():
110172
link = workos.client.portal.generate_link(
111173
organization=session["organization_id"], intent="audit_logs"
112174
)
113-
175+
114176
return redirect(link["link"])
115177

116178

Lines changed: 0 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,5 @@
11
from datetime import datetime
22

3-
user_signed_in = {
4-
"action": "user.signed_in",
5-
"occurred_at": datetime.now().isoformat(),
6-
"actor": {
7-
"type": "user",
8-
"id": "user_01GBNJC3MX9ZZJW1FSTF4C5938",
9-
},
10-
"targets": [
11-
{
12-
"type": "team",
13-
"id": "team_01GBNJD4MKHVKJGEWK42JNMBGS",
14-
},
15-
],
16-
"context": {
17-
"location": "123.123.123.123",
18-
"user_agent": "Chrome/104.0.0.0",
19-
},
20-
}
21-
22-
user_logged_out = {
23-
"action": "user.logged_out",
24-
"occurred_at": datetime.now().isoformat(),
25-
"actor": {
26-
"type": "user",
27-
"id": "user_01GBNJC3MX9ZZJW1FSTF4C5938",
28-
},
29-
"targets": [
30-
{
31-
"type": "team",
32-
"id": "team_01GBNJD4MKHVKJGEWK42JNMBGS",
33-
},
34-
],
35-
"context": {
36-
"location": "123.123.123.123",
37-
"user_agent": "Chrome/104.0.0.0",
38-
},
39-
}
40-
413
user_organization_set = {
424
"action": "user.organization_set",
435
"occurred_at": datetime.now().isoformat(),
@@ -56,41 +18,3 @@
5618
"user_agent": "Chrome/104.0.0.0",
5719
},
5820
}
59-
60-
user_organization_deleted = {
61-
"action": "user.organization_deleted",
62-
"occurred_at": datetime.now().isoformat(),
63-
"actor": {
64-
"type": "user",
65-
"id": "user_01GBNJC3MX9ZZJW1FSTF4C5938",
66-
},
67-
"targets": [
68-
{
69-
"type": "team",
70-
"id": "team_01GBNJD4MKHVKJGEWK42JNMBGS",
71-
},
72-
],
73-
"context": {
74-
"location": "123.123.123.123",
75-
"user_agent": "Chrome/104.0.0.0",
76-
},
77-
}
78-
79-
user_connection_deleted = {
80-
"action": "user.connection_deleted",
81-
"occurred_at": datetime.now().isoformat(),
82-
"actor": {
83-
"type": "user",
84-
"id": "user_01GBNJC3MX9ZZJW1FSTF4C5938",
85-
},
86-
"targets": [
87-
{
88-
"type": "team",
89-
"id": "team_01GBNJD4MKHVKJGEWK42JNMBGS",
90-
},
91-
],
92-
"context": {
93-
"location": "123.123.123.123",
94-
"user_agent": "Chrome/104.0.0.0",
95-
},
96-
}
Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1-
Flask==2.0.0
2-
workos>=1.20.2
3-
python-dotenv
1+
Flask==2.0.3
2+
Jinja2==3.1.1
3+
workos>=1.21.0
4+
python-dotenv
5+
flask-lucide==0.2.0
Loading
Binary file not shown.

0 commit comments

Comments
 (0)