Skip to content

Add 30 and 60 day notifications for expiring access tokens

What does this MR do and why?

Add 30 and 60 day notifications for expiring access tokens

In addition to the current 7 day notifications, this change will add 30 and 60 day email notifications for expiring personal access tokens. This also applies to notifications to owners for project and group access tokens.

For now, webhooks will only issue 7 day notifications, since currently there is not an easy way for users to determine from the payload if a notification is for 7, 30, or 60 days.

MR acceptance checklist

Please evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.

Database Review

Get user PATs (seven days interval)
SELECT "personal_access_tokens"."user_id",
     "personal_access_tokens"."expires_at",
     "personal_access_tokens"."id" AS t0_r0,
     "users"."id" AS t1_r0,
     "users"."email" AS t1_r1,
     "users"."encrypted_password" AS t1_r2,
     "users"."reset_password_token" AS t1_r3,
     "users"."reset_password_sent_at" AS t1_r4,
     "users"."remember_created_at" AS t1_r5,
     "users"."sign_in_count" AS t1_r6,
     "users"."current_sign_in_at" AS t1_r7,
     "users"."last_sign_in_at" AS t1_r8,
     "users"."current_sign_in_ip" AS t1_r9,
     "users"."last_sign_in_ip" AS t1_r10,
     "users"."created_at" AS t1_r11,
     "users"."updated_at" AS t1_r12,
     "users"."name" AS t1_r13,
     "users"."admin" AS t1_r14,
     "users"."projects_limit" AS t1_r15,
     "users"."failed_attempts" AS t1_r16,
     "users"."locked_at" AS t1_r17,
     "users"."username" AS t1_r18,
     "users"."can_create_group" AS t1_r19,
     "users"."can_create_team" AS t1_r20,
     "users"."state" AS t1_r21,
     "users"."color_scheme_id" AS t1_r22,
     "users"."password_expires_at" AS t1_r23,
     "users"."created_by_id" AS t1_r24,
     "users"."last_credential_check_at" AS t1_r25,
     "users"."avatar" AS t1_r26,
     "users"."confirmation_token" AS t1_r27,
     "users"."confirmed_at" AS t1_r28,
     "users"."confirmation_sent_at" AS t1_r29,
     "users"."unconfirmed_email" AS t1_r30,
     "users"."hide_no_ssh_key" AS t1_r31,
     "users"."admin_email_unsubscribed_at" AS t1_r32,
     "users"."notification_email" AS t1_r33,
     "users"."hide_no_password" AS t1_r34,
     "users"."password_automatically_set" AS t1_r35,
     "users"."encrypted_otp_secret" AS t1_r36,
     "users"."encrypted_otp_secret_iv" AS t1_r37,
     "users"."encrypted_otp_secret_salt" AS t1_r38,
     "users"."otp_required_for_login" AS t1_r39,
     "users"."otp_backup_codes" AS t1_r40,
     "users"."public_email" AS t1_r41,
     "users"."dashboard" AS t1_r42,
     "users"."project_view" AS t1_r43,
     "users"."consumed_timestep" AS t1_r44,
     "users"."layout" AS t1_r45,
     "users"."hide_project_limit" AS t1_r46,
     "users"."note" AS t1_r47,
     "users"."unlock_token" AS t1_r48,
     "users"."otp_grace_period_started_at" AS t1_r49,
     "users"."external" AS t1_r50,
     "users"."incoming_email_token" AS t1_r51,
     "users"."auditor" AS t1_r52,
     "users"."require_two_factor_authentication_from_group" AS t1_r53,
     "users"."two_factor_grace_period" AS t1_r54,
     "users"."last_activity_on" AS t1_r55,
     "users"."notified_of_own_activity" AS t1_r56,
     "users"."preferred_language" AS t1_r57,
     "users"."theme_id" AS t1_r58,
     "users"."accepted_term_id" AS t1_r59,
     "users"."feed_token" AS t1_r60,
     "users"."private_profile" AS t1_r61,
     "users"."roadmap_layout" AS t1_r62,
     "users"."include_private_contributions" AS t1_r63,
     "users"."commit_email" AS t1_r64,
     "users"."group_view" AS t1_r65,
     "users"."managing_group_id" AS t1_r66,
     "users"."first_name" AS t1_r67,
     "users"."last_name" AS t1_r68,
     "users"."static_object_token" AS t1_r69,
     "users"."role" AS t1_r70,
     "users"."user_type" AS t1_r71,
     "users"."static_object_token_encrypted" AS t1_r72,
     "users"."otp_secret_expires_at" AS t1_r73,
     "users"."onboarding_in_progress" AS t1_r74,
     "users"."color_mode_id" AS t1_r75,
     "users"."last_access_from_pipl_country_at" AS t1_r76
FROM "personal_access_tokens"
LEFT OUTER JOIN "users" ON "users"."id" = "personal_access_tokens"."user_id"
WHERE (revoked = FALSE
     AND seven_days_notification_sent_at IS NULL
     AND expire_notification_delivered = FALSE
     AND expires_at BETWEEN '2024-10-11' AND '2024-10-18')
AND "personal_access_tokens"."impersonation" = FALSE
AND "users"."user_type" = 0
ORDER BY "personal_access_tokens"."expires_at" ASC LIMIT 100

Details and visualization

Get user PATs (thirty day interval)
SELECT "personal_access_tokens"."user_id",
       "personal_access_tokens"."expires_at",
       "personal_access_tokens"."id" AS t0_r0,
       "users"."id" AS t1_r0,
       "users"."email" AS t1_r1,
       "users"."encrypted_password" AS t1_r2,
       "users"."reset_password_token" AS t1_r3,
       "users"."reset_password_sent_at" AS t1_r4,
       "users"."remember_created_at" AS t1_r5,
       "users"."sign_in_count" AS t1_r6,
       "users"."current_sign_in_at" AS t1_r7,
       "users"."last_sign_in_at" AS t1_r8,
       "users"."current_sign_in_ip" AS t1_r9,
       "users"."last_sign_in_ip" AS t1_r10,
       "users"."created_at" AS t1_r11,
       "users"."updated_at" AS t1_r12,
       "users"."name" AS t1_r13,
       "users"."admin" AS t1_r14,
       "users"."projects_limit" AS t1_r15,
       "users"."failed_attempts" AS t1_r16,
       "users"."locked_at" AS t1_r17,
       "users"."username" AS t1_r18,
       "users"."can_create_group" AS t1_r19,
       "users"."can_create_team" AS t1_r20,
       "users"."state" AS t1_r21,
       "users"."color_scheme_id" AS t1_r22,
       "users"."password_expires_at" AS t1_r23,
       "users"."created_by_id" AS t1_r24,
       "users"."last_credential_check_at" AS t1_r25,
       "users"."avatar" AS t1_r26,
       "users"."confirmation_token" AS t1_r27,
       "users"."confirmed_at" AS t1_r28,
       "users"."confirmation_sent_at" AS t1_r29,
       "users"."unconfirmed_email" AS t1_r30,
       "users"."hide_no_ssh_key" AS t1_r31,
       "users"."admin_email_unsubscribed_at" AS t1_r32,
       "users"."notification_email" AS t1_r33,
       "users"."hide_no_password" AS t1_r34,
       "users"."password_automatically_set" AS t1_r35,
       "users"."encrypted_otp_secret" AS t1_r36,
       "users"."encrypted_otp_secret_iv" AS t1_r37,
       "users"."encrypted_otp_secret_salt" AS t1_r38,
       "users"."otp_required_for_login" AS t1_r39,
       "users"."otp_backup_codes" AS t1_r40,
       "users"."public_email" AS t1_r41,
       "users"."dashboard" AS t1_r42,
       "users"."project_view" AS t1_r43,
       "users"."consumed_timestep" AS t1_r44,
       "users"."layout" AS t1_r45,
       "users"."hide_project_limit" AS t1_r46,
       "users"."note" AS t1_r47,
       "users"."unlock_token" AS t1_r48,
       "users"."otp_grace_period_started_at" AS t1_r49,
       "users"."external" AS t1_r50,
       "users"."incoming_email_token" AS t1_r51,
       "users"."auditor" AS t1_r52,
       "users"."require_two_factor_authentication_from_group" AS t1_r53,
       "users"."two_factor_grace_period" AS t1_r54,
       "users"."last_activity_on" AS t1_r55,
       "users"."notified_of_own_activity" AS t1_r56,
       "users"."preferred_language" AS t1_r57,
       "users"."theme_id" AS t1_r58,
       "users"."accepted_term_id" AS t1_r59,
       "users"."feed_token" AS t1_r60,
       "users"."private_profile" AS t1_r61,
       "users"."roadmap_layout" AS t1_r62,
       "users"."include_private_contributions" AS t1_r63,
       "users"."commit_email" AS t1_r64,
       "users"."group_view" AS t1_r65,
       "users"."managing_group_id" AS t1_r66,
       "users"."first_name" AS t1_r67,
       "users"."last_name" AS t1_r68,
       "users"."static_object_token" AS t1_r69,
       "users"."role" AS t1_r70,
       "users"."user_type" AS t1_r71,
       "users"."static_object_token_encrypted" AS t1_r72,
       "users"."otp_secret_expires_at" AS t1_r73,
       "users"."onboarding_in_progress" AS t1_r74,
       "users"."color_mode_id" AS t1_r75,
       "users"."last_access_from_pipl_country_at" AS t1_r76
FROM "personal_access_tokens"
LEFT OUTER JOIN "users" ON "users"."id" = "personal_access_tokens"."user_id"
WHERE (revoked = FALSE
       AND thirty_days_notification_sent_at IS NULL
       AND expire_notification_delivered = FALSE
       AND expires_at BETWEEN '2024-10-19' AND '2024-11-10')
  AND "personal_access_tokens"."impersonation" = FALSE
  AND "users"."user_type" = 0
ORDER BY "personal_access_tokens"."expires_at" ASC LIMIT 100

Details and visualization

Get token names (seven days interval)
SELECT "personal_access_tokens"."name"
FROM "personal_access_tokens"
WHERE (revoked = FALSE
       AND seven_days_notification_sent_at IS NULL
       AND expire_notification_delivered = FALSE
       AND expires_at BETWEEN '2024-10-11' AND '2024-10-18')
  AND "personal_access_tokens"."impersonation" = FALSE
  AND "personal_access_tokens"."user_id" = 1 LIMIT 100

Details and visualization

Update token notification columns (seven day interval)

UPDATE "personal_access_tokens"
SET "seven_days_notification_sent_at" = '2024-10-11 08:25:44.646546',
                                        "expire_notification_delivered" = TRUE
WHERE (revoked = FALSE
       AND seven_days_notification_sent_at IS NULL
       AND expire_notification_delivered = FALSE
       AND expires_at BETWEEN '2024-10-11' AND '2024-10-18')
  AND "personal_access_tokens"."impersonation" = FALSE
  AND "personal_access_tokens"."user_id" = 1

Details and visualization

Get Project and Group access tokens (seven days interval)
SELECT "personal_access_tokens"."id",
       "personal_access_tokens"."user_id",
       "personal_access_tokens"."expires_at",
       "personal_access_tokens"."id" AS t0_r0,
       "users"."id" AS t1_r0,
       "users"."email" AS t1_r1,
       "users"."encrypted_password" AS t1_r2,
       "users"."reset_password_token" AS t1_r3,
       "users"."reset_password_sent_at" AS t1_r4,
       "users"."remember_created_at" AS t1_r5,
       "users"."sign_in_count" AS t1_r6,
       "users"."current_sign_in_at" AS t1_r7,
       "users"."last_sign_in_at" AS t1_r8,
       "users"."current_sign_in_ip" AS t1_r9,
       "users"."last_sign_in_ip" AS t1_r10,
       "users"."created_at" AS t1_r11,
       "users"."updated_at" AS t1_r12,
       "users"."name" AS t1_r13,
       "users"."admin" AS t1_r14,
       "users"."projects_limit" AS t1_r15,
       "users"."failed_attempts" AS t1_r16,
       "users"."locked_at" AS t1_r17,
       "users"."username" AS t1_r18,
       "users"."can_create_group" AS t1_r19,
       "users"."can_create_team" AS t1_r20,
       "users"."state" AS t1_r21,
       "users"."color_scheme_id" AS t1_r22,
       "users"."password_expires_at" AS t1_r23,
       "users"."created_by_id" AS t1_r24,
       "users"."last_credential_check_at" AS t1_r25,
       "users"."avatar" AS t1_r26,
       "users"."confirmation_token" AS t1_r27,
       "users"."confirmed_at" AS t1_r28,
       "users"."confirmation_sent_at" AS t1_r29,
       "users"."unconfirmed_email" AS t1_r30,
       "users"."hide_no_ssh_key" AS t1_r31,
       "users"."admin_email_unsubscribed_at" AS t1_r32,
       "users"."notification_email" AS t1_r33,
       "users"."hide_no_password" AS t1_r34,
       "users"."password_automatically_set" AS t1_r35,
       "users"."encrypted_otp_secret" AS t1_r36,
       "users"."encrypted_otp_secret_iv" AS t1_r37,
       "users"."encrypted_otp_secret_salt" AS t1_r38,
       "users"."otp_required_for_login" AS t1_r39,
       "users"."otp_backup_codes" AS t1_r40,
       "users"."public_email" AS t1_r41,
       "users"."dashboard" AS t1_r42,
       "users"."project_view" AS t1_r43,
       "users"."consumed_timestep" AS t1_r44,
       "users"."layout" AS t1_r45,
       "users"."hide_project_limit" AS t1_r46,
       "users"."note" AS t1_r47,
       "users"."unlock_token" AS t1_r48,
       "users"."otp_grace_period_started_at" AS t1_r49,
       "users"."external" AS t1_r50,
       "users"."incoming_email_token" AS t1_r51,
       "users"."auditor" AS t1_r52,
       "users"."require_two_factor_authentication_from_group" AS t1_r53,
       "users"."two_factor_grace_period" AS t1_r54,
       "users"."last_activity_on" AS t1_r55,
       "users"."notified_of_own_activity" AS t1_r56,
       "users"."preferred_language" AS t1_r57,
       "users"."theme_id" AS t1_r58,
       "users"."accepted_term_id" AS t1_r59,
       "users"."feed_token" AS t1_r60,
       "users"."private_profile" AS t1_r61,
       "users"."roadmap_layout" AS t1_r62,
       "users"."include_private_contributions" AS t1_r63,
       "users"."commit_email" AS t1_r64,
       "users"."group_view" AS t1_r65,
       "users"."managing_group_id" AS t1_r66,
       "users"."first_name" AS t1_r67,
       "users"."last_name" AS t1_r68,
       "users"."static_object_token" AS t1_r69,
       "users"."role" AS t1_r70,
       "users"."user_type" AS t1_r71,
       "users"."static_object_token_encrypted" AS t1_r72,
       "users"."otp_secret_expires_at" AS t1_r73,
       "users"."onboarding_in_progress" AS t1_r74,
       "users"."color_mode_id" AS t1_r75,
       "users"."last_access_from_pipl_country_at" AS t1_r76
FROM "personal_access_tokens"
LEFT OUTER JOIN "users" ON "users"."id" = "personal_access_tokens"."user_id"
WHERE 1=1
  AND (revoked = FALSE
       AND seven_days_notification_sent_at IS NULL
       AND expire_notification_delivered = FALSE
       AND expires_at BETWEEN '2024-10-11' AND '2024-10-18')
  AND "personal_access_tokens"."impersonation" = FALSE
  AND "users"."user_type" = 6
ORDER BY "personal_access_tokens"."expires_at" ASC LIMIT 100

Details and visualization

Update bot notification delivered columns
UPDATE "personal_access_tokens"
SET "seven_days_notification_sent_at" = '2024-10-11 08:25:44.774862',
                                        "expire_notification_delivered" = TRUE
WHERE "personal_access_tokens"."id" IN
    (SELECT "personal_access_tokens"."id"
     FROM "personal_access_tokens"
     LEFT OUTER JOIN "users" ON "users"."id" = "personal_access_tokens"."user_id"
     WHERE 1=1
       AND (revoked = FALSE
            AND seven_days_notification_sent_at IS NULL
            AND expire_notification_delivered = FALSE
            AND expires_at BETWEEN '2024-10-11' AND '2024-10-18')
       AND "personal_access_tokens"."impersonation" = FALSE
       AND "users"."user_type" = 6
       AND 1=1
     ORDER BY "personal_access_tokens"."expires_at" ASC LIMIT 100)

Details and visualization


For reference, here are the related queries on master:

Get user PATs (current master)
SELECT "personal_access_tokens"."user_id",
       "personal_access_tokens"."id" AS t0_r0,
       "users"."id" AS t1_r0,
       "users"."email" AS t1_r1,
       "users"."encrypted_password" AS t1_r2,
       "users"."reset_password_token" AS t1_r3,
       "users"."reset_password_sent_at" AS t1_r4,
       "users"."remember_created_at" AS t1_r5,
       "users"."sign_in_count" AS t1_r6,
       "users"."current_sign_in_at" AS t1_r7,
       "users"."last_sign_in_at" AS t1_r8,
       "users"."current_sign_in_ip" AS t1_r9,
       "users"."last_sign_in_ip" AS t1_r10,
       "users"."created_at" AS t1_r11,
       "users"."updated_at" AS t1_r12,
       "users"."name" AS t1_r13,
       "users"."admin" AS t1_r14,
       "users"."projects_limit" AS t1_r15,
       "users"."failed_attempts" AS t1_r16,
       "users"."locked_at" AS t1_r17,
       "users"."username" AS t1_r18,
       "users"."can_create_group" AS t1_r19,
       "users"."can_create_team" AS t1_r20,
       "users"."state" AS t1_r21,
       "users"."color_scheme_id" AS t1_r22,
       "users"."password_expires_at" AS t1_r23,
       "users"."created_by_id" AS t1_r24,
       "users"."last_credential_check_at" AS t1_r25,
       "users"."avatar" AS t1_r26,
       "users"."confirmation_token" AS t1_r27,
       "users"."confirmed_at" AS t1_r28,
       "users"."confirmation_sent_at" AS t1_r29,
       "users"."unconfirmed_email" AS t1_r30,
       "users"."hide_no_ssh_key" AS t1_r31,
       "users"."admin_email_unsubscribed_at" AS t1_r32,
       "users"."notification_email" AS t1_r33,
       "users"."hide_no_password" AS t1_r34,
       "users"."password_automatically_set" AS t1_r35,
       "users"."encrypted_otp_secret" AS t1_r36,
       "users"."encrypted_otp_secret_iv" AS t1_r37,
       "users"."encrypted_otp_secret_salt" AS t1_r38,
       "users"."otp_required_for_login" AS t1_r39,
       "users"."otp_backup_codes" AS t1_r40,
       "users"."public_email" AS t1_r41,
       "users"."dashboard" AS t1_r42,
       "users"."project_view" AS t1_r43,
       "users"."consumed_timestep" AS t1_r44,
       "users"."layout" AS t1_r45,
       "users"."hide_project_limit" AS t1_r46,
       "users"."note" AS t1_r47,
       "users"."unlock_token" AS t1_r48,
       "users"."otp_grace_period_started_at" AS t1_r49,
       "users"."external" AS t1_r50,
       "users"."incoming_email_token" AS t1_r51,
       "users"."auditor" AS t1_r52,
       "users"."require_two_factor_authentication_from_group" AS t1_r53,
       "users"."two_factor_grace_period" AS t1_r54,
       "users"."last_activity_on" AS t1_r55,
       "users"."notified_of_own_activity" AS t1_r56,
       "users"."preferred_language" AS t1_r57,
       "users"."theme_id" AS t1_r58,
       "users"."accepted_term_id" AS t1_r59,
       "users"."feed_token" AS t1_r60,
       "users"."private_profile" AS t1_r61,
       "users"."roadmap_layout" AS t1_r62,
       "users"."include_private_contributions" AS t1_r63,
       "users"."commit_email" AS t1_r64,
       "users"."group_view" AS t1_r65,
       "users"."managing_group_id" AS t1_r66,
       "users"."first_name" AS t1_r67,
       "users"."last_name" AS t1_r68,
       "users"."static_object_token" AS t1_r69,
       "users"."role" AS t1_r70,
       "users"."user_type" AS t1_r71,
       "users"."static_object_token_encrypted" AS t1_r72,
       "users"."otp_secret_expires_at" AS t1_r73,
       "users"."onboarding_in_progress" AS t1_r74,
       "users"."color_mode_id" AS t1_r75,
       "users"."last_access_from_pipl_country_at" AS t1_r76
FROM "personal_access_tokens"
LEFT OUTER JOIN "users" ON "users"."id" = "personal_access_tokens"."user_id"
WHERE ((revoked = FALSE
        AND expire_notification_delivered = FALSE
        AND expires_at >= CURRENT_DATE
        AND expires_at <= '2024-09-27')
       AND impersonation = FALSE)
  AND "users"."user_type" = 0 LIMIT 100

Details and visualization

Update user tokens delivery column (master)
UPDATE "personal_access_tokens"
SET "expire_notification_delivered" = TRUE
WHERE "personal_access_tokens"."user_id" = 1
  AND ((revoked = FALSE
        AND expire_notification_delivered = FALSE
        AND expires_at >= CURRENT_DATE
        AND expires_at <= '2024-09-27')
       AND impersonation = FALSE)

Details and visualization

Select project and resource access tokens (master)
SELECT "personal_access_tokens"."id",
       "personal_access_tokens"."user_id",
       "personal_access_tokens"."id" AS t0_r0,
       "users"."id" AS t1_r0,
       "users"."email" AS t1_r1,
       "users"."encrypted_password" AS t1_r2,
       "users"."reset_password_token" AS t1_r3,
       "users"."reset_password_sent_at" AS t1_r4,
       "users"."remember_created_at" AS t1_r5,
       "users"."sign_in_count" AS t1_r6,
       "users"."current_sign_in_at" AS t1_r7,
       "users"."last_sign_in_at" AS t1_r8,
       "users"."current_sign_in_ip" AS t1_r9,
       "users"."last_sign_in_ip" AS t1_r10,
       "users"."created_at" AS t1_r11,
       "users"."updated_at" AS t1_r12,
       "users"."name" AS t1_r13,
       "users"."admin" AS t1_r14,
       "users"."projects_limit" AS t1_r15,
       "users"."failed_attempts" AS t1_r16,
       "users"."locked_at" AS t1_r17,
       "users"."username" AS t1_r18,
       "users"."can_create_group" AS t1_r19,
       "users"."can_create_team" AS t1_r20,
       "users"."state" AS t1_r21,
       "users"."color_scheme_id" AS t1_r22,
       "users"."password_expires_at" AS t1_r23,
       "users"."created_by_id" AS t1_r24,
       "users"."last_credential_check_at" AS t1_r25,
       "users"."avatar" AS t1_r26,
       "users"."confirmation_token" AS t1_r27,
       "users"."confirmed_at" AS t1_r28,
       "users"."confirmation_sent_at" AS t1_r29,
       "users"."unconfirmed_email" AS t1_r30,
       "users"."hide_no_ssh_key" AS t1_r31,
       "users"."admin_email_unsubscribed_at" AS t1_r32,
       "users"."notification_email" AS t1_r33,
       "users"."hide_no_password" AS t1_r34,
       "users"."password_automatically_set" AS t1_r35,
       "users"."encrypted_otp_secret" AS t1_r36,
       "users"."encrypted_otp_secret_iv" AS t1_r37,
       "users"."encrypted_otp_secret_salt" AS t1_r38,
       "users"."otp_required_for_login" AS t1_r39,
       "users"."otp_backup_codes" AS t1_r40,
       "users"."public_email" AS t1_r41,
       "users"."dashboard" AS t1_r42,
       "users"."project_view" AS t1_r43,
       "users"."consumed_timestep" AS t1_r44,
       "users"."layout" AS t1_r45,
       "users"."hide_project_limit" AS t1_r46,
       "users"."note" AS t1_r47,
       "users"."unlock_token" AS t1_r48,
       "users"."otp_grace_period_started_at" AS t1_r49,
       "users"."external" AS t1_r50,
       "users"."incoming_email_token" AS t1_r51,
       "users"."auditor" AS t1_r52,
       "users"."require_two_factor_authentication_from_group" AS t1_r53,
       "users"."two_factor_grace_period" AS t1_r54,
       "users"."last_activity_on" AS t1_r55,
       "users"."notified_of_own_activity" AS t1_r56,
       "users"."preferred_language" AS t1_r57,
       "users"."theme_id" AS t1_r58,
       "users"."accepted_term_id" AS t1_r59,
       "users"."feed_token" AS t1_r60,
       "users"."private_profile" AS t1_r61,
       "users"."roadmap_layout" AS t1_r62,
       "users"."include_private_contributions" AS t1_r63,
       "users"."commit_email" AS t1_r64,
       "users"."group_view" AS t1_r65,
       "users"."managing_group_id" AS t1_r66,
       "users"."first_name" AS t1_r67,
       "users"."last_name" AS t1_r68,
       "users"."static_object_token" AS t1_r69,
       "users"."role" AS t1_r70,
       "users"."user_type" AS t1_r71,
       "users"."static_object_token_encrypted" AS t1_r72,
       "users"."otp_secret_expires_at" AS t1_r73,
       "users"."onboarding_in_progress" AS t1_r74,
       "users"."color_mode_id" AS t1_r75,
       "users"."last_access_from_pipl_country_at" AS t1_r76
FROM "personal_access_tokens"
LEFT OUTER JOIN "users" ON "users"."id" = "personal_access_tokens"."user_id"
WHERE 1=1
  AND ((revoked = FALSE
        AND expire_notification_delivered = FALSE
        AND expires_at >= CURRENT_DATE
        AND expires_at <= '2024-09-27')
       AND impersonation = FALSE)
  AND "users"."user_type" = 6 LIMIT 100

Details and visualization

Screenshots or screen recordings

Screenshots are required for UI changes, and strongly recommended for all other merge requests.

Before After

How to set up and validate locally

  1. Enable the feature: Feature.enable(:expiring_pats_30d_60d_notifications)
  2. Go to your account Personal Access Tokens: https://gdk.test:3443/-/user_settings/personal_access_tokens
  3. Create 3 access tokens:
    1. expiring in 5 days
    2. expiring in 20 days
    3. expiring in 55 days
  4. Go to access tokens for a project you own (with Ultimate plan if emulating SaaS): https://gdk.test:3443/flightjs/Flight/-/settings/access_tokens
  5. Create 3 access tokens:
    1. expiring in 5 days
    2. expiring in 20 days
    3. expiring in 55 days
  6. Go to access tokens for a group you own (with Ultimate plan if emulating SaaS): https://gdk.test:3443/flightjs/-/settings/access_tokens
    1. expiring in 5 days
    2. expiring in 20 days
    3. expiring in 55 days
  7. Run the worker from the Rails console: PersonalAccessTokens::ExpiringWorker.new.perform or enqueue from https://gdk.test:3443/admin/sidekiq/cron
  8. Go to letter_opener and check for notifications about the expiring tokens: https://gdk.test:3443/rails/letter_opener
  9. If you don't see the emails, you can validate that the mailers are enqueued in Sidekiq at https://gdk.test:3443/admin/sidekiq/queues/mailers

Related to #464040 (closed)

Edited by Andrew Evans

Merge request reports

Loading