-
-
Notifications
You must be signed in to change notification settings - Fork 4.2k
/
createuser.py
212 lines (169 loc) · 6.25 KB
/
createuser.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
from __future__ import annotations
from typing import TYPE_CHECKING
import click
from sentry.runner.decorators import configuration
if TYPE_CHECKING:
from django.db.models.fields import Field
from sentry.users.models.user import User
def _get_field(field_name: str) -> Field[str, str]:
from django.db.models.fields import Field
from sentry.users.models.user import User
ret = User._meta.get_field(field_name)
assert isinstance(ret, Field), ret
return ret
def _get_email() -> list[str]:
from django.core.exceptions import ValidationError
rv = click.prompt("Email")
field = _get_field("email")
try:
return [field.clean(rv, None)]
except ValidationError as e:
raise click.ClickException("; ".join(e.messages))
def _get_password() -> str:
from django.core.exceptions import ValidationError
rv = click.prompt("Password", hide_input=True, confirmation_prompt=True)
field = _get_field("password")
try:
return field.clean(rv, None)
except ValidationError as e:
raise click.ClickException("; ".join(e.messages))
def _get_superuser() -> bool:
return click.confirm("Should this user be a superuser?", default=False)
def _set_superadmin(user: User) -> None:
"""
superadmin role approximates superuser (model attribute) but leveraging
Sentry's role system.
"""
from sentry.users.models.userrole import UserRole, UserRoleUser
role = UserRole.objects.get(name="Super Admin")
UserRoleUser.objects.create(user=user, role=role)
@click.command()
@click.option(
"--email",
"emails",
multiple=True,
default=None,
help="Email(s) to create account(s) for.",
)
@click.option(
"--org-id",
default=None,
help="Org ID to add users to, if not provided will use the default Org.",
)
@click.option("--password")
@click.option(
"--superuser/--no-superuser",
default=None,
is_flag=True,
help="Superusers have full access to Sentry, across all organizations.",
)
@click.option(
"--staff/--no-staff",
default=None,
is_flag=True,
help="Staff users have access to Django backend.",
)
@click.option("--no-password", default=False, is_flag=True)
@click.option("--no-input", default=False, is_flag=True)
@click.option(
"--force-update", default=False, is_flag=True, help="If true, will update existing users."
)
@configuration
def createuser(
emails: list[str] | None,
org_id: str | None,
password: str | None,
superuser: bool | None,
staff: bool | None,
no_password: bool,
no_input: bool,
force_update: bool,
) -> None:
"Create a new user."
from django.conf import settings
if not no_input:
if not emails:
emails = _get_email()
if not (password or no_password):
password = _get_password()
if superuser is None:
superuser = _get_superuser()
if superuser is None:
superuser = False
# Prevent a user from being set to staff without superuser
if not superuser and staff:
click.echo("Non-superuser asked to be given staff access, correcting to staff=False")
staff = False
# Default staff to match the superuser setting
if staff is None:
staff = superuser
# Verify we have an email to work with.
if not emails:
raise click.ClickException("Invalid or missing email address.")
if not no_password and not password:
raise click.ClickException("No password set and --no-password not passed.")
from sentry import roles
from sentry.users.models.user import User
# Loop through the email list provided.
for email in emails:
fields = dict(
email=email,
username=email,
is_superuser=superuser,
is_staff=staff,
is_active=True,
)
verb = None
try:
user = User.objects.get(username=email)
except User.DoesNotExist:
user = None
# Update the user if they already exist.
if user is not None:
if force_update:
user.update(**fields)
verb = "updated"
else:
click.echo(f"User: {email} exists, use --force-update to force.")
continue
# Create a new user if they don't already exist.
else:
user = User.objects.create(**fields)
verb = "created"
# TODO(dcramer): kill this when we improve flows
if settings.SENTRY_SINGLE_ORGANIZATION:
from sentry.organizations.services.organization import organization_service
# Get the org if specified, otherwise use the default.
if org_id:
org_context = organization_service.get_organization_by_id(
id=org_id, include_teams=False, include_projects=False
)
if org_context is None:
raise Exception("Organization ID not found")
org = org_context.organization
else:
org = organization_service.get_default_organization()
if superuser:
role = roles.get_top_dog().id
else:
role = org.default_role
member = organization_service.add_organization_member(
organization_id=org.id,
default_org_role=org.default_role,
user_id=user.id,
role=role,
)
# if we've only got a single team let's go ahead and give
# access to that team as its likely the desired outcome
team = organization_service.get_single_team(organization_id=org.id)
if team is not None:
organization_service.add_team_member(
organization_id=org.id, team_id=team.id, organization_member_id=member.id
)
click.echo(f"Added to organization: {org.slug}")
if password:
user.set_password(password)
user.save()
if superuser and (settings.SENTRY_SELF_HOSTED or settings.SENTRY_SINGLE_ORGANIZATION):
_set_superadmin(user)
click.echo(f"User {verb}: {email}")