Skip to content

Commit e3cf39f

Browse files
authored
feat: add name and token_last_characters to apitoken (#58945)
- Add a nullable `name` column to the `ApiToken` model to help users identify and give meaningful names to their API tokens. - Add a nullable `last_token_characters` column that will contain the last four characters of the API token to help users with identify the token in the UI when the majority of it is obfuscated. Supports #58918
1 parent e0a04bb commit e3cf39f

File tree

8 files changed

+386
-293
lines changed

8 files changed

+386
-293
lines changed

migrations_lockfile.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,5 @@ feedback: 0003_feedback_add_env
99
hybridcloud: 0008_add_externalactorreplica
1010
nodestore: 0002_nodestore_no_dictfield
1111
replays: 0003_add_size_to_recording_segment
12-
sentry: 0583_add_early_adopter_to_organization_mapping
12+
sentry: 0584_apitoken_add_name_and_last_chars
1313
social_auth: 0002_default_auto_field

src/sentry/backup/comparators.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -666,7 +666,9 @@ def get_default_comparators():
666666
default_comparators: ComparatorMap = defaultdict(
667667
list,
668668
{
669-
"sentry.apitoken": [HashObfuscatingComparator("refresh_token", "token")],
669+
"sentry.apitoken": [
670+
HashObfuscatingComparator("refresh_token", "token", "token_last_characters")
671+
],
670672
"sentry.apiapplication": [HashObfuscatingComparator("client_id", "client_secret")],
671673
"sentry.authidentity": [HashObfuscatingComparator("ident", "token")],
672674
"sentry.alertrule": [DateUpdatedComparator("date_modified")],
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Generated by Django 3.2.20 on 2023-10-27 17:53
2+
3+
from django.db import migrations, models
4+
5+
from sentry.new_migrations.migrations import CheckedMigration
6+
7+
8+
class Migration(CheckedMigration):
9+
# This flag is used to mark that a migration shouldn't be automatically run in production. For
10+
# the most part, this should only be used for operations where it's safe to run the migration
11+
# after your code has deployed. So this should not be used for most operations that alter the
12+
# schema of a table.
13+
# Here are some things that make sense to mark as dangerous:
14+
# - Large data migrations. Typically we want these to be run manually by ops so that they can
15+
# be monitored and not block the deploy for a long period of time while they run.
16+
# - Adding indexes to large tables. Since this can take a long time, we'd generally prefer to
17+
# have ops run this and not block the deploy. Note that while adding an index is a schema
18+
# change, it's completely safe to run the operation after the code has deployed.
19+
is_dangerous = False
20+
21+
dependencies = [
22+
("sentry", "0583_add_early_adopter_to_organization_mapping"),
23+
]
24+
25+
operations = [
26+
migrations.AddField(
27+
model_name="apitoken",
28+
name="name",
29+
field=models.CharField(max_length=255, null=True),
30+
),
31+
migrations.AddField(
32+
model_name="apitoken",
33+
name="token_last_characters",
34+
field=models.CharField(max_length=4, null=True),
35+
),
36+
]

src/sentry/models/apitoken.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,9 @@ class ApiToken(ReplicatedControlModel, HasApiScopes):
3737
# users can generate tokens without being application-bound
3838
application = FlexibleForeignKey("sentry.ApiApplication", null=True)
3939
user = FlexibleForeignKey("sentry.User")
40+
name = models.CharField(max_length=255, null=True)
4041
token = models.CharField(max_length=64, unique=True, default=generate_token)
42+
token_last_characters = models.CharField(max_length=4, null=True)
4143
refresh_token = models.CharField(max_length=64, unique=True, null=True, default=generate_token)
4244
expires_at = models.DateTimeField(null=True, default=default_expiration)
4345
date_added = models.DateTimeField(default=timezone.now)
@@ -55,6 +57,16 @@ class Meta:
5557
def __str__(self):
5658
return force_str(self.token)
5759

60+
# TODO(mdtro): uncomment this function after 0583_apitoken_add_name_and_last_chars migration has been applied
61+
# def save(self, *args: Any, **kwargs: Any) -> None:
62+
# # when a new ApiToken is created we take the last four characters of the token
63+
# # and save them in the `token_last_characters` field so users can identify
64+
# # tokens in the UI where they're mostly obfuscated
65+
# token_last_characters = self.token[-4:]
66+
# self.token_last_characters = token_last_characters
67+
68+
# return super().save(**kwargs)
69+
5870
def outbox_region_names(self) -> Collection[str]:
5971
return list(find_all_region_names())
6072

src/sentry/testutils/helpers/backups.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -550,7 +550,10 @@ def create_exhaustive_sentry_app(self, name: str, owner: User, org: Organization
550550
# Api*
551551
ApiAuthorization.objects.create(application=app.application, user=owner)
552552
ApiToken.objects.create(
553-
application=app.application, user=owner, token=uuid4().hex, expires_at=None
553+
application=app.application,
554+
user=owner,
555+
expires_at=None,
556+
name="create_exhaustive_sentry_app",
554557
)
555558
ApiGrant.objects.create(
556559
user=owner,
@@ -583,7 +586,9 @@ def create_exhaustive_global_configs(self, owner: User):
583586
self.create_exhaustive_global_configs_regional()
584587
ControlOption.objects.create(key="bar", value="b")
585588
ApiAuthorization.objects.create(user=owner)
586-
ApiToken.objects.create(user=owner, token=uuid4().hex, expires_at=None)
589+
ApiToken.objects.create(
590+
user=owner, expires_at=None, name="create_exhaustive_global_configs"
591+
)
587592

588593
@assume_test_silo_mode(SiloMode.REGION)
589594
def create_exhaustive_global_configs_regional(self):

0 commit comments

Comments
 (0)