• 
      

    Add formal support for service accounts.

    Review Request #15105 — Created June 7, 2026 and updated

    Information

    Review Board
    release-8.x

    Reviewers

    Review Bot and now the Doc Converter both create users designed for
    automated communication with the Review Board API. Currently, both need
    to create the users manually, and they both do this differently, with
    different resulting states.

    These won't be our last such accounts, and we currently don't have a way
    of distinguishing between these and normal users, which means they get
    added to subscription licenses when they shouldn't.

    This change formalizes all of this with a new ServiceAccount object.
    These can be subclassed or instantiated with information on the account
    (a stable but unique service account registration ID, a name, e-mail
    address, avatars, preferred username (if available), API token policy,
    and more). They can then be registered in a central registry.

    Upon registration, a user account will be fetched or created for the
    account and made available for later use. The logic for figuring out the
    user account is probably the largest part of this change. It does the
    following:

    1. If the registration claims a user (which should only be done if
      there's state saying it once created that user for this purpose), it
      will be fetched and populated with state to identify it as a service
      account user.

    2. If not claiming a user, it will fetch the preferred username and see
      if it's marked with identifying state. If so, it returns it. Else, it
      adds a number to the end and tries again.

    3. If a username is scanned and not found, it will be chosen as the
      user.

    Any user that's picked for the service account will have a
    service_account_id in Profile.extra_data, which will aid in finding
    the user in the future. Extensions are advised to store the resulting
    username and claim it for future use, to speed up the loading process.

    Due to the way that user creation works, it's now possible to listen for
    post_save signal for a user and check if it corresponds to a
    ServiceAccount. This means we can avoid auto-adding new service
    accounts to licenses.

    Once a ServiceAccount is registered, both the user and an API token
    can be accessed for use. This will use a client API token, identifying
    the token as being owned by this ServiceAccount, and with a minimum
    validity window of 5 hours (meaning if an existing token is found that
    has less time than this, a new token will be created to help avoid
    integration issues).

    If profile or API token state needs to be forcefully updated, the
    extension can bump one of the two version fields (one for profile, one
    for token information), which will force an update.

    A User Details Provider now exists to badge service accounts with
    "Service Account", so that they can be easily distinguished whenever
    they might be shown.

    Tested registering a ServiceAccount for a test user and verified
    the user account was claimed and the user badged.

    Tested registering without a user claim and verified it scanned and
    created a suitable user.

    Unit tests pass.

    Summary ID
    Add formal support for service accounts.
    Review Bot and now the Doc Converter both create users designed for automated communication with the Review Board API. Currently, both need to create the users manually, and they both do this differently, with different resulting states. These won't be our last such accounts, and we currently don't have a way of distinguishing between these and normal users, which means they get added to subscription licenses when they shouldn't. This change formalizes all of this with a new `ServiceAccount` object. These can be subclassed or instantiated with information on the account (a stable but unique service account registration ID, a name, e-mail address, avatars, preferred username (if available), API token policy, and more). They can then be registered in a central registry. Upon registration, a user account will be fetched or created for the account and made available for later use. The logic for figuring out the user account is probably the largest part of this change. It does the following: 1. If the registration claims a user (which should only be done if there's state saying it once created that user for this purpose), it will be fetched and populated with state to identify it as a service account user. 2. If not claiming a user, it will fetch the preferred username and see if it's marked with identifying state. If so, it returns it. Else, it adds a number to the end and tries again. 3. If a username is scanned and not found, it will be chosen as the user. Any user that's picked for the service account will have a `service_account_id` in `Profile.extra_data`, which will aid in finding the user in the future. Extensions are advised to store the resulting username and claim it for future use, to speed up the loading process. Due to the way that user creation works, it's now possible to listen for `post_save` signal for a user and check if it corresponds to a `ServiceAccount`. This means we can avoid auto-adding new service accounts to licenses. Once a `ServiceAccount` is registered, both the user and an API token can be accessed for use. This will use a client API token, identifying the token as being owned by this `ServiceAccount`, and with a minimum validity window of 5 hours (meaning if an existing token is found that has less time than this, a new token will be created to help avoid integration issues). If profile or API token state needs to be forcefully updated, the extension can bump one of the two version fields (one for profile, one for token information), which will force an update. A User Details Provider now exists to badge service accounts with "Service Account", so that they can be easily distinguished whenever they might be shown.
    0cf5bb955484cac5c4945dc632169feefb23427c
    Description From Last Updated

    local variable 'user' is assigned to but never used Column: 13 Error code: F841

    reviewbot reviewbot

    'datetime.timedelta' imported but unused Column: 1 Error code: F401

    reviewbot reviewbot

    'django.utils.timezone' imported but unused Column: 1 Error code: F401

    reviewbot reviewbot

    line too long (88 > 79 characters) Column: 80 Error code: E501

    reviewbot reviewbot

    redefinition of unused 'Iterator' from line 11 Column: 5 Error code: F811

    reviewbot reviewbot

    This is assigned None but never read. Were you intending on using this as a cache in get_api_token()? If you …

    david david

    Typo: thaat -> that

    david david

    If two services want the same username ("bot" for example), this will stomp over an existing one. We should check …

    david david

    Typo: Pleaase -> Please

    david david

    I don't see how profile could be falsy at this point.

    david david

    __init__ already defauts self.email to be mail_default_from, so the contents of this block are an unreachable duplicate of what we …

    david david

    last_name is a CharField(blank=True, null=False). Having None here works okay for sqlite but will fail on MySQL and Postgres.

    david david

    minimum subclass -> minimum init

    david david

    should mention subclass here (right now this has the same docstring as test_init_with_override_all)

    david david

    Should be ServiceAccountRegistry

    david david
    Checks run (1 failed, 1 succeeded)
    flake8 failed.
    JSHint passed.

    flake8

    chipx86
    Review request changed
    Change Summary:

    Removed unused variables and imports.

    Commits:
    Summary ID
    Add formal support for service accounts.
    Review Bot and now the Doc Converter both create users designed for automated communication with the Review Board API. Currently, both need to create the users manually, and they both do this differently, with different resulting states. These won't be our last such accounts, and we currently don't have a way of distinguishing between these and normal users, which means they get added to subscription licenses when they shouldn't. This change formalizes all of this with a new `ServiceAccount` object. These can be subclassed or instantiated with information on the account (a stable but unique service account registration ID, a name, e-mail address, avatars, preferred username (if available), API token policy, and more). They can then be registered in a central registry. Upon registration, a user account will be fetched or created for the account and made available for later use. The logic for figuring out the user account is probably the largest part of this change. It does the following: 1. If the registration claims a user (which should only be done if there's state saying it once created that user for this purpose), it will be fetched and populated with state to identify it as a service account user. 2. If not claiming a user, it will fetch the preferred username and see if it's marked with identifying state. If so, it returns it. Else, it adds a number to the end and tries again. 3. If a username is scanned and not found, it will be chosen as the user. Any user that's picked for the service account will have a `service_account_id` in `Profile.extra_data`, which will aid in finding the user in the future. Extensions are advised to store the resulting username and claim it for future use, to speed up the loading process. Due to the way that user creation works, it's now possible to listen for `post_save` signal for a user and check if it corresponds to a `ServiceAccount`. This means we can avoid auto-adding new service accounts to licenses. Once a `ServiceAccount` is registered, both the user and an API token can be accessed for use. This will use a client API token, identifying the token as being owned by this `ServiceAccount`, and with a minimum validity window of 5 hours (meaning if an existing token is found that has less time than this, a new token will be created to help avoid integration issues). If profile or API token state needs to be forcefully updated, the extension can bump one of the two version fields (one for profile, one for token information), which will force an update. A User Details Provider now exists to badge service accounts with "Service Account", so that they can be easily distinguished whenever they might be shown.
    819d46e96258ff2da4d1e5d3e9c6fadcb8987537
    Add formal support for service accounts.
    Review Bot and now the Doc Converter both create users designed for automated communication with the Review Board API. Currently, both need to create the users manually, and they both do this differently, with different resulting states. These won't be our last such accounts, and we currently don't have a way of distinguishing between these and normal users, which means they get added to subscription licenses when they shouldn't. This change formalizes all of this with a new `ServiceAccount` object. These can be subclassed or instantiated with information on the account (a stable but unique service account registration ID, a name, e-mail address, avatars, preferred username (if available), API token policy, and more). They can then be registered in a central registry. Upon registration, a user account will be fetched or created for the account and made available for later use. The logic for figuring out the user account is probably the largest part of this change. It does the following: 1. If the registration claims a user (which should only be done if there's state saying it once created that user for this purpose), it will be fetched and populated with state to identify it as a service account user. 2. If not claiming a user, it will fetch the preferred username and see if it's marked with identifying state. If so, it returns it. Else, it adds a number to the end and tries again. 3. If a username is scanned and not found, it will be chosen as the user. Any user that's picked for the service account will have a `service_account_id` in `Profile.extra_data`, which will aid in finding the user in the future. Extensions are advised to store the resulting username and claim it for future use, to speed up the loading process. Due to the way that user creation works, it's now possible to listen for `post_save` signal for a user and check if it corresponds to a `ServiceAccount`. This means we can avoid auto-adding new service accounts to licenses. Once a `ServiceAccount` is registered, both the user and an API token can be accessed for use. This will use a client API token, identifying the token as being owned by this `ServiceAccount`, and with a minimum validity window of 5 hours (meaning if an existing token is found that has less time than this, a new token will be created to help avoid integration issues). If profile or API token state needs to be forcefully updated, the extension can bump one of the two version fields (one for profile, one for token information), which will force an update. A User Details Provider now exists to badge service accounts with "Service Account", so that they can be easily distinguished whenever they might be shown.
    0cf5bb955484cac5c4945dc632169feefb23427c

    Checks run (2 succeeded)

    flake8 passed.
    JSHint passed.
    david
    1. 
        
    2. Show all issues

      This is assigned None but never read. Were you intending on using this as a cache in get_api_token()?

      If you keep it, it should be listed in # Instance variables too.

    3. Show all issues

      Typo: thaat -> that

    4. Show all issues

      If two services want the same username ("bot" for example), this will stomp over an existing one.

      We should check if claim_map.get(username) is not either None or self, and skip if so.

      This situation should get a unit test two.

    5. Show all issues

      Typo: Pleaase -> Please

    6. Show all issues

      I don't see how profile could be falsy at this point.

    7. reviewboard/accounts/service_accounts.py (Diff revision 2)
       
       
       
       
      Show all issues

      __init__ already defauts self.email to be mail_default_from, so the contents of this block are an unreachable duplicate of what we already have done.

    8. Show all issues

      last_name is a CharField(blank=True, null=False). Having None here works okay for sqlite but will fail on MySQL and Postgres.

    9. Show all issues

      minimum subclass -> minimum init

    10. Show all issues

      should mention subclass here (right now this has the same docstring as test_init_with_override_all)

    11. Show all issues

      Should be ServiceAccountRegistry

    12.