Bläddra i källkod

Add a mechanism to allow user to set the expiration date on API tokens

This expiration date can be at most 2 years and defaults to 6 months.

When renewing a API token, use the default 6 months expiration.

Adjust the unit-tests for this.

Fixes https://pagure.io/pagure/issue/2416

Signed-off-by: Pierre-Yves Chibon <pingou@pingoured.fr>
Pierre-Yves Chibon 4 år sedan
förälder
incheckning
bea040cb26

+ 7 - 1
pagure/api/project.py

@@ -2397,13 +2397,19 @@ def api_project_create_api_token(repo, namespace=None, username=None):
     if form.validate_on_submit():
         acls = form.acls.data
         description = form.description.data
+        expiration_date = form.expiration_date.data
     else:
         raise pagure.exceptions.APIError(
             400, error_code=APIERROR.EINVALIDREQ, errors=form.errors
         )
 
     token = pagure.lib.query.add_token_to_user(
-        flask.g.session, project, acls, flask.g.fas_user.user, description
+        flask.g.session,
+        project=project,
+        acls=acls,
+        username=flask.g.fas_user.user,
+        description=description,
+        expiration_date=expiration_date,
     )
     output = {"token": {"description": token.description, "id": token.id}}
 

+ 23 - 1
pagure/cli/admin.py

@@ -191,6 +191,9 @@ def _parser_admin_token_create(subparser):
         "create", help="Create a new API token"
     )
     local_parser.add_argument("user", help="User to associate with the token")
+    local_parser.add_argument(
+        "expiration_date", help="Expiration date for the new token"
+    )
     local_parser.set_defaults(func=do_create_admin_token)
 
 
@@ -828,6 +831,19 @@ def do_create_admin_token(args):
     # Validate user first
     pagure.lib.query.get_user(session, args.user)
 
+    # Validate the expiration date
+    try:
+        expiration_date = arrow.get(
+            args.expiration_date, "YYYY-MM-DD"
+        ).replace(tzinfo="UTC")
+        expiration_date = expiration_date.date()
+    except Exception as err:
+        _log.exception(err)
+        raise pagure.exceptions.PagureException(
+            "Invalid expiration date submitted: %s, not of the format "
+            "YYYY-MM-DD" % args.expiration_date
+        )
+
     acls_list = pagure.config.config["ADMIN_API_ACLS"]
     for idx, acl in enumerate(acls_list):
         print("%s.  %s" % (idx, acl))
@@ -844,7 +860,13 @@ def do_create_admin_token(args):
     print("Do you want to create this API token?")
     if _ask_confirmation():
         print(
-            pagure.lib.query.add_token_to_user(session, None, acls, args.user)
+            pagure.lib.query.add_token_to_user(
+                session,
+                project=None,
+                acls=acls,
+                username=args.user,
+                expiration_date=expiration_date,
+            )
         )
 
 

+ 5 - 0
pagure/forms.py

@@ -442,6 +442,11 @@ class NewTokenForm(PagureForm):
     description = wtforms.StringField(
         "description", [wtforms.validators.Optional()]
     )
+    expiration_date = wtforms.DateField(
+        "expiration date",
+        [wtforms.validators.DataRequired()],
+        default=datetime.date.today() + datetime.timedelta(days=(30 * 6)),
+    )
     acls = wtforms.SelectMultipleField(
         "ACLs", [wtforms.validators.DataRequired()], choices=[]
     )

+ 11 - 2
pagure/lib/query.py

@@ -4297,7 +4297,9 @@ def get_acls(session, restrict=None):
     return query.all()
 
 
-def add_token_to_user(session, project, acls, username, description=None):
+def add_token_to_user(
+    session, project, acls, username, expiration_date, description=None
+):
     """ Create a new token for the specified user on the specified project
     with the given ACLs.
     """
@@ -4305,12 +4307,19 @@ def add_token_to_user(session, project, acls, username, description=None):
 
     user = search_user(session, username=username)
 
+    if expiration_date > (
+        datetime.date.today() + datetime.timedelta(days=730)
+    ):
+        raise pagure.exceptions.PagureException(
+            "API tokens can only be created up to 2 years"
+        )
+
     token = pagure.lib.model.Token(
         id=pagure.lib.login.id_generator(64),
         user_id=user.id,
         project_id=project.id if project else None,
         description=description,
-        expiration=datetime.datetime.utcnow() + datetime.timedelta(days=60),
+        expiration=expiration_date,
     )
     session.add(token)
     session.flush()

+ 2 - 0
pagure/templates/add_token.html

@@ -35,6 +35,8 @@
           {% endif %}
           {{ render_bootstrap_field(
               form.description, field_description="Small description of this API token") }}
+          {{ render_bootstrap_field(
+              form.expiration_date, field_description="Expiration date for this API token, maximum 2 year, format: YYYY-MM-DD") }}
           <strong><label for="acls">ACLs</label></strong><br/>
 
           {% for acl in acls %}

+ 5 - 0
pagure/ui/app.py

@@ -1498,12 +1498,15 @@ def add_api_user_token():
                 description=form.description.data.strip() or None,
                 acls=form.acls.data,
                 username=user.username,
+                expiration_date=form.expiration_date.data,
             )
             flask.g.session.commit()
             flask.flash("Token created")
             return flask.redirect(
                 flask.url_for("ui_ns.user_settings") + "#nav-api-tab"
             )
+        except pagure.exceptions.PagureException as err:
+            flask.flash(str(err), "error")
         except SQLAlchemyError as err:  # pragma: no cover
             flask.g.session.rollback()
             _log.exception(err)
@@ -1582,6 +1585,8 @@ def renew_api_user_token(token_id):
                 description=token.description or None,
                 acls=acls,
                 username=flask.g.fas_user.username,
+                expiration_date=datetime.date.today()
+                + datetime.timedelta(days=(30 * 6)),
             )
             flask.g.session.commit()
             flask.flash("Token created")

+ 5 - 0
pagure/ui/repo.py

@@ -2349,6 +2349,7 @@ def add_token(repo, username=None, namespace=None):
                 description=form.description.data.strip() or None,
                 acls=form.acls.data,
                 username=flask.g.fas_user.username,
+                expiration_date=form.expiration_date.data,
             )
             flask.g.session.commit()
             flask.flash("Token created")
@@ -2418,6 +2419,8 @@ def renew_api_token(repo, token_id, username=None, namespace=None):
                 description=token.description or None,
                 acls=acls,
                 username=flask.g.fas_user.username,
+                expiration_date=datetime.date.today()
+                + datetime.timedelta(days=(30 * 6)),
             )
             flask.g.session.commit()
             flask.flash("Token created")
@@ -2430,6 +2433,8 @@ def renew_api_token(repo, token_id, username=None, namespace=None):
                 )
                 + "#apikeys-tab"
             )
+        except pagure.exceptions.PagureException as err:
+            flask.flash(str(err), "error")
         except SQLAlchemyError as err:  # pragma: no cover
             flask.g.session.rollback()
             _log.exception(err)

+ 71 - 9
tests/test_pagure_admin.py

@@ -49,7 +49,13 @@ class PagureAdminAdminTokenEmptytests(tests.Modeltests):
         """ Test the do_create_admin_token function of pagure-admin without
         user.
         """
-        args = munch.Munch({"user": "pingou"})
+        exp_date = datetime.date.today() + datetime.timedelta(days=300)
+        args = munch.Munch(
+            {
+                "user": "pingou",
+                "expiration_date": exp_date.strftime("%Y-%m-%d"),
+            }
+        )
         with self.assertRaises(pagure.exceptions.PagureException) as cm:
             pagure.cli.admin.do_create_admin_token(args)
         self.assertEqual(cm.exception.args[0], 'No user "pingou" found')
@@ -239,7 +245,13 @@ class PagureAdminAdminTokentests(tests.Modeltests):
         conf.return_value = True
         rinp.return_value = "1,2,3"
 
-        args = munch.Munch({"user": "pingou"})
+        exp_date = datetime.date.today() + datetime.timedelta(days=300)
+        args = munch.Munch(
+            {
+                "user": "pingou",
+                "expiration_date": exp_date.strftime("%Y-%m-%d"),
+            }
+        )
         pagure.cli.admin.do_create_admin_token(args)
 
         # Check the outcome
@@ -267,7 +279,13 @@ class PagureAdminAdminTokentests(tests.Modeltests):
         conf.return_value = True
         rinp.return_value = "1,2,3"
 
-        args = munch.Munch({"user": "pingou"})
+        exp_date = datetime.date.today() + datetime.timedelta(days=300)
+        args = munch.Munch(
+            {
+                "user": "pingou",
+                "expiration_date": exp_date.strftime("%Y-%m-%d"),
+            }
+        )
         pagure.cli.admin.do_create_admin_token(args)
 
         # Retrieve all tokens
@@ -305,11 +323,13 @@ class PagureAdminAdminTokentests(tests.Modeltests):
     def test_do_list_admin_token_non_admin_acls(self):
         """ Test the do_list_admin_token function of pagure-admin for a token
         without any admin ACL. """
+        exp_date = datetime.date.today() + datetime.timedelta(days=300)
         pagure.lib.query.add_token_to_user(
             self.session,
             project=None,
             acls=["issue_assign", "pull_request_subscribe"],
             username="pingou",
+            expiration_date=exp_date,
         )
 
         # Retrieve all admin tokens
@@ -352,7 +372,13 @@ class PagureAdminAdminTokentests(tests.Modeltests):
         conf.return_value = True
         rinp.return_value = "2,4,5"
 
-        args = munch.Munch({"user": "pingou"})
+        exp_date = datetime.date.today() + datetime.timedelta(days=300)
+        args = munch.Munch(
+            {
+                "user": "pingou",
+                "expiration_date": exp_date.strftime("%Y-%m-%d"),
+            }
+        )
         pagure.cli.admin.do_create_admin_token(args)
 
         # Retrieve the token
@@ -391,11 +417,13 @@ class PagureAdminAdminTokentests(tests.Modeltests):
     def test_do_info_admin_token_non_admin_acl(self):
         """ Test the do_info_admin_token function of pagure-admin for a
         token not having any admin ACL. """
+        exp_date = datetime.date.today() + datetime.timedelta(days=300)
         pagure.lib.query.add_token_to_user(
             self.session,
             project=None,
             acls=["issue_assign", "pull_request_subscribe"],
             username="pingou",
+            expiration_date=exp_date,
         )
 
         # Retrieve the token
@@ -438,7 +466,13 @@ class PagureAdminAdminTokentests(tests.Modeltests):
         conf.return_value = True
         rinp.return_value = "1,2,3"
 
-        args = munch.Munch({"user": "pingou"})
+        exp_date = datetime.date.today() + datetime.timedelta(days=300)
+        args = munch.Munch(
+            {
+                "user": "pingou",
+                "expiration_date": exp_date.strftime("%Y-%m-%d"),
+            }
+        )
         pagure.cli.admin.do_create_admin_token(args)
 
         # Retrieve the token
@@ -506,11 +540,13 @@ class PagureAdminAdminTokentests(tests.Modeltests):
         conf.return_value = True
         rinp.return_value = "1,2,3"
 
+        exp_date = datetime.date.today() + datetime.timedelta(days=300)
         pagure.lib.query.add_token_to_user(
             self.session,
             project=None,
             acls=["issue_assign", "pull_request_subscribe"],
             username="pingou",
+            expiration_date=exp_date,
         )
 
         # Retrieve all tokens to get the one of interest
@@ -561,7 +597,13 @@ class PagureAdminAdminTokentests(tests.Modeltests):
         conf.return_value = True
         rinp.return_value = "1,2,3"
 
-        args = munch.Munch({"user": "pingou"})
+        exp_date = datetime.date.today() + datetime.timedelta(days=300)
+        args = munch.Munch(
+            {
+                "user": "pingou",
+                "expiration_date": exp_date.strftime("%Y-%m-%d"),
+            }
+        )
         pagure.cli.admin.do_create_admin_token(args)
 
         # Retrieve the token
@@ -602,7 +644,13 @@ class PagureAdminAdminTokentests(tests.Modeltests):
         conf.return_value = True
         rinp.return_value = "1,2,3"
 
-        args = munch.Munch({"user": "pingou"})
+        exp_date = datetime.date.today() + datetime.timedelta(days=300)
+        args = munch.Munch(
+            {
+                "user": "pingou",
+                "expiration_date": exp_date.strftime("%Y-%m-%d"),
+            }
+        )
         pagure.cli.admin.do_create_admin_token(args)
 
         # Retrieve the token
@@ -645,7 +693,13 @@ class PagureAdminAdminTokentests(tests.Modeltests):
         conf.return_value = True
         rinp.return_value = "1,2,3"
 
-        args = munch.Munch({"user": "pingou"})
+        exp_date = datetime.date.today() + datetime.timedelta(days=300)
+        args = munch.Munch(
+            {
+                "user": "pingou",
+                "expiration_date": exp_date.strftime("%Y-%m-%d"),
+            }
+        )
         pagure.cli.admin.do_create_admin_token(args)
 
         # Retrieve the token
@@ -691,7 +745,13 @@ class PagureAdminAdminTokentests(tests.Modeltests):
         conf.return_value = True
         rinp.return_value = "1,2,3"
 
-        args = munch.Munch({"user": "pingou"})
+        exp_date = datetime.date.today() + datetime.timedelta(days=300)
+        args = munch.Munch(
+            {
+                "user": "pingou",
+                "expiration_date": exp_date.strftime("%Y-%m-%d"),
+            }
+        )
         pagure.cli.admin.do_create_admin_token(args)
 
         # Retrieve the token
@@ -773,11 +833,13 @@ class PagureAdminAdminTokentests(tests.Modeltests):
         conf.return_value = True
         rinp.return_value = "1,2,3"
 
+        exp_date = datetime.date.today() + datetime.timedelta(days=300)
         pagure.lib.query.add_token_to_user(
             self.session,
             project=None,
             acls=["issue_assign", "pull_request_subscribe"],
             username="pingou",
+            expiration_date=exp_date,
         )
 
         # Retrieve all tokens to get the one of interest

+ 40 - 6
tests/test_pagure_flask_api_project.py

@@ -4017,8 +4017,13 @@ class PagureFlaskApiProjectCreateAPITokenTests(tests.Modeltests):
         self.session.commit()
 
         # Create modify_project token for foo user
+        exp_date = datetime.date.today() + datetime.timedelta(days=300)
         token = pagure.lib.query.add_token_to_user(
-            self.session, project=None, acls=["modify_project"], username="foo"
+            self.session,
+            project=None,
+            acls=["modify_project"],
+            username="foo",
+            expiration_date=exp_date,
         )
 
         # Call the connector with foo user token and verify content
@@ -4060,8 +4065,13 @@ class PagureFlaskApiProjectCreateAPITokenTests(tests.Modeltests):
         self.session.commit()
 
         # Create modify_project token for foo user
+        exp_date = datetime.date.today() + datetime.timedelta(days=300)
         pagure.lib.query.add_token_to_user(
-            self.session, project=None, acls=["create_branch"], username="foo"
+            self.session,
+            project=None,
+            acls=["create_branch"],
+            username="foo",
+            expiration_date=exp_date,
         )
         mtoken = pagure.lib.query.search_token(
             self.session, ["create_branch"], user="foo"
@@ -4099,8 +4109,13 @@ class PagureFlaskApiProjectCreateAPITokenTests(tests.Modeltests):
         self.session.commit()
 
         # Create modify_project token for foo user
+        exp_date = datetime.date.today() + datetime.timedelta(days=300)
         pagure.lib.query.add_token_to_user(
-            self.session, project=None, acls=["modify_project"], username="foo"
+            self.session,
+            project=None,
+            acls=["modify_project"],
+            username="foo",
+            expiration_date=exp_date,
         )
         mtoken = pagure.lib.query.search_token(
             self.session, ["modify_project"], user="foo"
@@ -4140,11 +4155,13 @@ class PagureFlaskApiProjectConnectorTests(tests.Modeltests):
         project = pagure.lib.query._get_project(self.session, "test")
 
         # Create witness project Token for pingou user
+        exp_date = datetime.date.today() + datetime.timedelta(days=300)
         pagure.lib.query.add_token_to_user(
             self.session,
             project=project,
             acls=["pull_request_merge"],
             username="pingou",
+            expiration_date=exp_date,
         )
         ctokens = pagure.lib.query.search_token(
             self.session, ["pull_request_merge"], user="pingou"
@@ -4190,19 +4207,26 @@ class PagureFlaskApiProjectConnectorTests(tests.Modeltests):
         self.session.commit()
 
         # Create modify_project token for foo user
+        exp_date = datetime.date.today() + datetime.timedelta(days=300)
         pagure.lib.query.add_token_to_user(
-            self.session, project=None, acls=["modify_project"], username="foo"
+            self.session,
+            project=None,
+            acls=["modify_project"],
+            username="foo",
+            expiration_date=exp_date,
         )
         mtoken = pagure.lib.query.search_token(
             self.session, ["modify_project"], user="foo"
         )[0]
 
         # Create witness project Token for foo user
+        exp_date = datetime.date.today() + datetime.timedelta(days=300)
         pagure.lib.query.add_token_to_user(
             self.session,
             project=project,
             acls=["pull_request_merge"],
             username="foo",
+            expiration_date=exp_date,
         )
         ctokens = pagure.lib.query.search_token(
             self.session, ["pull_request_merge"], user="foo"
@@ -4250,8 +4274,13 @@ class PagureFlaskApiProjectConnectorTests(tests.Modeltests):
         self.session.commit()
 
         # Create modify_project token for foo user
+        exp_date = datetime.date.today() + datetime.timedelta(days=300)
         pagure.lib.query.add_token_to_user(
-            self.session, project=None, acls=["create_project"], username="foo"
+            self.session,
+            project=None,
+            acls=["create_project"],
+            username="foo",
+            expiration_date=exp_date,
         )
         mtoken = pagure.lib.query.search_token(
             self.session, ["create_project"], user="foo"
@@ -4280,8 +4309,13 @@ class PagureFlaskApiProjectConnectorTests(tests.Modeltests):
         self.session.commit()
 
         # Create modify_project token for foo user
+        exp_date = datetime.date.today() + datetime.timedelta(days=300)
         pagure.lib.query.add_token_to_user(
-            self.session, project=None, acls=["modify_project"], username="foo"
+            self.session,
+            project=None,
+            acls=["modify_project"],
+            username="foo",
+            expiration_date=exp_date,
         )
         mtoken = pagure.lib.query.search_token(
             self.session, ["modify_project"], user="foo"

+ 2 - 1
tests/test_pagure_flask_internal.py

@@ -60,8 +60,9 @@ class PagureFlaskInternaltests(tests.Modeltests):
         )
         self.assertEqual(output.status_code, 401)
         # correct token => will work
+        exp_date = datetime.date.today() + datetime.timedelta(days=300)
         pagure.lib.query.add_token_to_user(
-            self.session, None, ["internal_access"], "pingou"
+            self.session, None, ["internal_access"], "pingou", exp_date
         )
         token = pagure.lib.query.search_token(
             self.session, acls=["internal_access"], user="pingou"

+ 15 - 17
tests/test_pagure_flask_ui_app.py

@@ -2269,7 +2269,7 @@ class PagureFlaskApptests(tests.Modeltests):
             self.assertIn("Token created", output_text)
             self.assertEqual(
                 output_text.count(
-                    '<small class="font-weight-bold text-success input-group-text">Active until'
+                    '<small class="font-weight-bold">Active until'
                 ),
                 1,
             )
@@ -2325,7 +2325,7 @@ class PagureFlaskApptests(tests.Modeltests):
             )
             self.assertEqual(
                 output_text.count(
-                    '<small class="font-weight-bold text-success input-group-text">Active until'
+                    '<small class="font-weight-bold">Active until'
                 ),
                 1,
             )
@@ -2349,7 +2349,7 @@ class PagureFlaskApptests(tests.Modeltests):
             )
             self.assertEqual(
                 output_text.count(
-                    '<small class="font-weight-bold text-success input-group-text">Active until'
+                    '<small class="font-weight-bold">Active until'
                 ),
                 0,
             )
@@ -2371,7 +2371,7 @@ class PagureFlaskApptests(tests.Modeltests):
             )
             self.assertEqual(
                 output_text.count(
-                    '<small class="font-weight-bold text-success input-group-text">Active until'
+                    '<small class="font-weight-bold">Active until'
                 ),
                 0,
             )
@@ -2589,7 +2589,6 @@ class PagureFlaskAppNoTicketstests(tests.Modeltests):
 
 
 class PagureFlaskAppRenewUserApiTokentests(tests.Modeltests):
-
     @patch("pagure.decorators.admin_session_timedout")
     def setUp(self, ast):
         """ Constructor """
@@ -2598,7 +2597,7 @@ class PagureFlaskAppRenewUserApiTokentests(tests.Modeltests):
         self.ast = ast
         self.ast.return_value = False
 
-        user = tests.FakeUser(username = "pingou")
+        user = tests.FakeUser(username="pingou")
         with tests.user_set(self.app.application, user):
             output = self.app.get("/settings/token/new")
             self.assertEqual(output.status_code, 200)
@@ -2608,10 +2607,7 @@ class PagureFlaskAppRenewUserApiTokentests(tests.Modeltests):
             self.csrf_token = self.get_csrf(output=output)
 
             # Create a token to renew
-            data = {
-                "csrf_token": self.csrf_token,
-                "acls": ["modify_project"]
-            }
+            data = {"csrf_token": self.csrf_token, "acls": ["modify_project"]}
             output = self.app.post(
                 "/settings/token/new/", data=data, follow_redirects=True
             )
@@ -2625,7 +2621,7 @@ class PagureFlaskAppRenewUserApiTokentests(tests.Modeltests):
         self.assertEqual(
             userobj.tokens[0].expiration.date(),
             datetime.datetime.utcnow().date()
-            + datetime.timedelta(days=60),
+            + datetime.timedelta(days=(30 * 6)),
         )
 
         self.token = userobj.tokens[0].id
@@ -2641,7 +2637,7 @@ class PagureFlaskAppRenewUserApiTokentests(tests.Modeltests):
         """ Test the renew_api_token endpoint. """
         ast.return_value = True
 
-        user = tests.FakeUser(username = "pingou")
+        user = tests.FakeUser(username="pingou")
         with tests.user_set(self.app.application, user):
             data = {"csrf_token": self.csrf_token}
 
@@ -2656,7 +2652,7 @@ class PagureFlaskAppRenewUserApiTokentests(tests.Modeltests):
     def test_renew_api_token_invalid_token(self):
         """ Test the renew_api_token endpoint. """
 
-        user = tests.FakeUser(username = "pingou")
+        user = tests.FakeUser(username="pingou")
         with tests.user_set(self.app.application, user):
             output = self.app.post(
                 "/settings/token/renew/123",
@@ -2669,7 +2665,7 @@ class PagureFlaskAppRenewUserApiTokentests(tests.Modeltests):
     def test_renew_api_token(self):
         """ Test the renew_api_token endpoint. """
 
-        user = tests.FakeUser(username = "pingou")
+        user = tests.FakeUser(username="pingou")
         with tests.user_set(self.app.application, user):
 
             output = self.app.post(
@@ -2679,7 +2675,7 @@ class PagureFlaskAppRenewUserApiTokentests(tests.Modeltests):
             )
             output_text = output.get_data(as_text=True)
             self.assertIn(
-                "<title>pingou\'s settings - Pagure</title>", output_text
+                "<title>pingou's settings - Pagure</title>", output_text
             )
             self.assertIn("Token created", output_text)
             self.assertEqual(output_text.count('title="Revoke token">'), 2)
@@ -2687,7 +2683,9 @@ class PagureFlaskAppRenewUserApiTokentests(tests.Modeltests):
             self.session.commit()
 
             # Existing token has been renewed
-            userobj = pagure.lib.query.search_user(self.session, username="pingou")
+            userobj = pagure.lib.query.search_user(
+                self.session, username="pingou"
+            )
             self.assertEqual(len(userobj.tokens), 2)
             self.assertEqual(
                 userobj.tokens[0].expiration.date(),
@@ -2695,7 +2693,7 @@ class PagureFlaskAppRenewUserApiTokentests(tests.Modeltests):
             )
             self.assertEqual(
                 userobj.tokens[0].created.date(),
-                userobj.tokens[1].created.date()
+                userobj.tokens[1].created.date(),
             )
             self.assertEqual(userobj.tokens[0].acls, userobj.tokens[1].acls)
             self.assertEqual(

+ 4 - 7
tests/test_pagure_flask_ui_repo.py

@@ -5588,10 +5588,7 @@ index 0000000..fb7093d
             )
             self.assertIn("<strong> Test token</strong>", output_text)
             self.assertIn(
-                '<span class="input-group-text text-success">'
-                '\n                    <small class="font-weight-bold">'
-                "Active until",
-                output_text,
+                '<small class="font-weight-bold">Active until', output_text
             )
 
     @patch("pagure.decorators.admin_session_timedout")
@@ -5663,7 +5660,7 @@ index 0000000..fb7093d
             self.assertEqual(
                 repo.tokens[0].expiration.date(),
                 datetime.datetime.utcnow().date()
-                + datetime.timedelta(days=60),
+                + datetime.timedelta(days=(30 * 6)),
             )
 
             token = repo.tokens[0].id
@@ -5763,7 +5760,7 @@ index 0000000..fb7093d
             self.assertEqual(
                 repo.tokens[0].expiration.date(),
                 datetime.datetime.utcnow().date()
-                + datetime.timedelta(days=60),
+                + datetime.timedelta(days=(30 * 6)),
             )
 
             token = repo.tokens[0].id
@@ -5778,7 +5775,7 @@ index 0000000..fb7093d
             )
             self.assertIn("Token created", output_text)
             self.assertEqual(output_text.count('title="Revoke token">'), 2)
-            self.assertEqual(output_text.count('title="Renew token">'), 0)
+            self.assertEqual(output_text.count('title="Renew token">'), 2)
 
             # Existing token has been renewed
             self.session.commit()