Account API

This is the documentation for the Python client for the MaIS Account API. Everything on this page is located in the stanford.mais.account module.

To begin using the MaIS Account API, you should first instantiate a MAISClient object. Once that is done, you can use it to instantiate an stanford.mais.account.AccountClient.

Note

All Account instances are read-only. You cannot use this SDK to create or change accounts.

Account API Client

You will use the AccountClient instance to access the Account API. Instantiating a AccountClient is easy:

client = MAISClient(...)
wclient = AccountClient(client)

For performance, the AccountClient maintains a cache of fetched accounts. To clear the cache, call clear_cache().

Warning

Once you clear the cache, avoid using any Account instances that you might have used before!

Fetching Accounts

Once you have a AccountClient, there are several ways you can access accounts. The accounts you access will be instances of the Account class.

One way is to use the get() method to fetch an accounts, providing the username of the accounts that you want.

Important

Usernames are all lowercase.

The AccountClient also implements the __getitem__ method, so instead of calling get() you can get accounts as you would items from a dict.

Here is an example of the two ways you can get a account:

aclient = AccountClient(...)

# These two operations give you the same Workgroup:
lelandjr = aclient.get('lelandjr')
lelandjr = aclient['lelandjr']

Account Views

Stanford accounts represent more than just people. “Functional Accounts” exist for services that need to act as an account. Most applications only care about people, so Account Views may be used to limit an AccountClient to just the types of accounts that you want.

An Account View is enabled by calling the appropriate AccountClient method. There are four Account Views available:

  • only_active(): Limits fetching and existance tests to only active accounts.

  • only_inactive(): Limits fetching and existance tests to only inactive accounts.

  • only_people(): Limits fetching and existance tests to only accounts for people.

  • only_functional(): Limits fetching and existance tests to only functional accounts.

Each method returns a modified AccountClient, whose get(), __getitem__, and __contains__ methods will only ‘see’ accounts matching the view you requested. If you try to call get() for an account that does not match the view you requested, a KeyError will be raised.

Tip

Account Views can be chained together!

See the following example of Account Views in action:

# `lelandjr` is an inactive person account;
# `functional` is an active functional account.

aclient = AccountClient(...)
'lelandjr' in aclient # True
'functional' in aclient # True

# Make an Account View limited to people:
aclient_people = aclient.only_people()
'lelandjr' in aclient_people # True
'functional' in aclient_people # False

# Account Views can be 'chained' together:
aclient_active_people = aclient.only_people().only_active()
'lelandjr' in aclient_active_people # False
'functional' in aclient_active_people # False

# To an Account View, non-matching accounts do not exist:
lelandjr1 = aclient['lelandjr'] # works fine
lelandjr2 = aclient_active_people['lelandjr'] # KeyError

Searching for Accounts

The Account API provides limited support for searching for accounts. You are only able to check if an account exists, and you are only able to search for accounts that have recently changed status.

Account Existence

Instances of AccountClient implement Container functionality, so you can check for account existence like so:

aclient = AccountClient(...)
lelandjr_exists = (True if 'lelandjr' in aclient else False)

Warning

This check respects any Account Views you have in place. If you do an in check on an Account View, you might get False even if the account actually exists!

Accounts who Changed Status

The Account API provides a way to search for accounts which have recently changed status (where “recently” means “from 1 to 30 days ago”). You can perform this search by calling the get_changed_status() method. The method takes three parameters:

  • days: How far back to search, from 1 to 30.

  • current_status: One of active, inactive, or pending, only accounts with this current status will be included in the results.

    Note

    pending is a status that accounts take when they are first created, and are not yet active.

    Important

    This search only counts the current status. If an account has changed from active to inactive and back to active in the last 3 days, a search with days=5 and current_status='inactive' will not include that account; the account had gone inactive within the search period, but it is not inactive now.

  • get_people: If True, only accounts for people are returned; if False, only functional accounts are returned.

The result of the search is a set, so if you need to run the search multiple times, you can combine results using unions.

This search returns “lite” account records, not full account records. These records are represented by the PartialAccount class, which contains the following properties:

  • sunetid: The account’s username.

  • is_person: True if the account is for a person; False otherwise.

  • is_active: True if the account is active; False otherwise.

  • last_update: The last time the account was updated, as an aware datetime.

If you need the full Account, you can fetch it by calling the account() method.

For example, here is code that runs a search and outputs a list of usernames (which are included in lite account records) and names (which are not):

aclient = AccountClient(...)

results1 = aclient.get_changed_status(
  days=7,
  current_status='active',
  get_people=True
)
results2 = aclient.get_changed_status(
  days=7,
  current_status='active',
  get_people=False,
)

print("New accounts in the last 7 days:")
for result in (results1 | results2):
  account = result.account(aclient)
  print(f"* {result.sunetid} ({account.name})")

SUNetID Validation

If you have a collection of SUNetIDs, and you want to confirm that they are all SUNetIDs, you can use the validation function stanford.mais.account.validate.validate(), which is in the stanford.mais.account.validate module.

The function takes two parameters:

  1. Either a string, or a collection — a list, tuple, set, or frozenset — of strings. If you provide a string, items in the string must be separated by whitespace.

  2. An AccountClient.

    Warning

    If you provide an Account View, validation will take that into account, and may provide unexpected results!

validate() returns an AccountValidationResults, with usernames sorted into four properties:

  • full: The SUNetIDs of active, full-service (or full-sponsored) accounts.

  • base: The SUNetIDs of active, base-service (or base-sponsored) accounts.

  • inactive: The SUNetIDs of inactive accounts.

  • unknown: Usernames that are either functional accounts, or that are not accounts.

Each property is a collection of strings, where each string is a SUNetID (except for unknown, where the strings might be functional account usernames).

Account Class

The Account class represents “full” account records. They are obtained by using get(), or when upgrading a PartialAccount “lite” account record using account().

Properties

Every Account has properties, all of which are read-only.

Note

This section provides summaries only. See the Module Documentation for details about each property!

  • sunetid: The account’s username. For accounts for people, this is a SUNetID.

  • name: The account’s name.

  • description: For people, this is some combination of their Org name and position title; for functional accounts, this is a description set at the time of the account’s creation.

  • is_person: If True, this is an account for a person; False otherwise.

  • is_active: If True, this account is active and may use Stanford services.

    Important

    Frozen person accounts are still considered active.

  • last_update: The datetime when the account (not the Account instance, the actual underlying account) was last changed.

Account Services

In this context, a “service” represents a core University service that is made available to an account. For example, active accounts for people always have a kerberos service, while active functional accounts for the Shared Email service all have a seas service.

Services may have “settings” associated with them, which are key-value pairs. Keys may be optional, and may be multi-valued.

Note

When a service is not active, all service-specific settings become optional.

Warning

When a service is inactive, not only do all service-specific settings become optional, but some services may have settings un-set when they go inactive.

To access the services for an account, obtain a Account instance for the account, and access the services property.

The services property is a AccountServiceTypes, which itself contains one property for every service. If an account has the service, the property will contain something; if the account does not have the service, the property will be None.

Important

Just because a service does not exist for an account, does not mean that it never existed for an account.

Here is an example of how to look up an account’s UNIX User ID (UID) number. If the account does not have the kerberos service, or the service is not active, then None is returned:

def get_uid(
  account: Account,
) -> int | None:
  if account.services.kerberos is None:
    return None
  elif account.services.kerberos.is_active is False:
    return None
  else:
    return account.services.kerberos.uid

Each service has its own class, subclasses of AccountService. This class, and its subclasses, exist in the stanford.mais.account.service module.

Service Status

Every service contains a status property, which will return a ServiceStatus. There are three possible statuses:

  • active: The service is active for the account.

  • frozen: The account has the service, but may not use it at this time.

  • inactive: This account does not have this service.

The is_active property will tell you if a service is active.

Warning

Because of the existence of the frozen status, it is not safe to assume that if is_active is False, that the service is inactive.

Instead, consider using not_inactive.

Other Services

These are services which will be of interest to specific groups of developers.

Note

This section provides summaries only. See the Module Documentation for details about each service!

Email

AccountServiceTypes property name: email
Service class: AccountServiceEmail

If active, the account has a Stanford personal electronic mailbox. The seas service should also be active.

All this service’s settings are obsolete, and should not be used.

Leland

AccountServiceTypes property name: leland
Service class: AccountServiceLeland

Originally created for Stanford’s Shared Computing environment, today it represents access to FarmShare.

All active full-service (and full-sponsored) accounts for people will have this service. This service is another way to tell if a person has a base or full account.

The service has one required, single-valued setting:

  • shell: The absolute path to the user’s shell.

PTS

AccountServiceTypes property name: pts
Service class: AccountServicePTS

This represents the accounts entry in the AFS ir cell’s Protection Server. People and CGI functional accounts will have this service. This is required for the account to access any AFS service at Stanford.

The service has one required, single-value setting:

  • uid: The UID number for the account. It should be the same as the account’s UID number in the kerberos service.

AFS

AccountServiceTypes property name: afs
Service class: AccountServiceAFS

This represents an account’s AFS home volume. CGI functional accounts might also have this service.

The service has one required, single-value setting:

  • homedirectory: The absolute path to the account’s AFS home directory in the ir cell.

Library

AccountServiceTypes property name: library
Service class: AccountServiceLibrary

This represents access to library e-resources, which full-service accounts receive automatically, and which sponsored accounts may have granted to them.

The service has no settings.

Obsolete Services

Two services are obsolete. Accounts might still have one or more of these services; they are listed here, and documented in Module Documentation.

Module Documentation

stanford.mais.account

This module contains the AccountClient, used to access the Account API.

class stanford.mais.account.AccountClient(client: ~stanford.mais.client.MAISClient, _cache: ~collections.abc.MutableMapping[str, ~stanford.mais.account.account.Account] = <factory>, _filters: frozenset[~collections.abc.Callable[[~stanford.mais.account.account.Account], bool]] = <factory>)[source]

The AccountClient is the second thing you will instantiate when you want to interact with the MaIS Account API. (The first thing you instantiate is a MAISClient). Once you have a MAISClient, you pass it to the AccountClient constructor with this parameter:

Parameters:

client (stanford.mais.client.MAISClient) – The MAIS client to use.

Once you have an AccountClient instantiated, you can use get() to fetch an account. For your convenience, instances of this class also implement __getitem__, so instead of doing…

aclient = AccountClient(...)
lelandjr = client.get('lelandjr')

… you can do …

aclient = AccountClient(...)
lelandjr = client['lelandjr']

Instances also implement Container functionality, so you can check for account existence like so:

aclient = AccountClient(...)
lelandjr_exists = (True if 'lelandjr' in aclient else False)

Through the use of caching, if you then decide to fetch the account after confirming its existance, the entry will be served from cache instead of making a fresh API request.

Warning

The existence of an account does not mean it is active!

client: MAISClient

A MAISClient instance.

This configures the API endpoint (accessed via client.urls['account']) and client key/cert to use. It must be provided when calling the class constructor.

Raises:

TypeError – A client was not provided.

get(sunetid: str) Account[source]

Fetch an Account.

This is a convenience wrapper around Account.get(). All other parameters provided are passed through to get(), and the resulting instance is returned.

Refer to Account.get() for details on parameters, exceptions, etc..

clear_cache() None[source]

Clear cache of accounts.

As mentioned in the class docs, visited accounts are cached locally, for speed and to reduce load on the Account API. Although accounts rarely change, in long-running programs, this can be a problem. To assist, this method clears the cache of this specific Account client.

Danger

If you are holding a reference to an existing Account, or to one of the the Account’s services, clearing the cache does not invalidate those references!

This method should not be called unless you know what you are doing.

only_active() AccountClient[source]

Create a modified AccountClient that can only see active accounts.

The returned client instance has been modified so that get() only returns active accounts. If you try to look up an inactive account, get() will act as if the account does not exist.

As many Account API consumers are only interested in active accounts, you may find this to be very convenient. If this interests you, you can do something like this:

import stanford.mais.client
from stanford.mais.client.account import AccountClient

api_client = stanford.mais.client.MAISClient(...)
active_accounts = AccountClient(api_client).only_active()

Tip

You can stack these filters. For example, AccountClient(api_client).only_active().only_people() will only be able to “see” active people accounts (ignoring active functional accounts).

Warning

The ‘client’ returned by this method uses the same caches as this client. Therefore, it must not be used across threads/processes.

only_inactive() AccountClient[source]

Create a modified AccountClient that can only see inactive accounts.

The returned client instance has been modified so that get() only returns inactive accounts. If you try to look up an active account, get() will act as if the account does not exist.

See only_active() for examples, tips, and warnings.

only_people() AccountClient[source]

Create a modified AccountClient that can only see accounts of people.

The returned client instance has been modified so that get() only returns the accounts of people. If you try to look up a functional account, get() will act as if the account does not exist.

As many Account API consumers are only interested in SUNetIDs, you may find this to be very convenient. If this interests you, you can do something like this:

import stanford.mais.client
from stanford.mais.client.account import AccountClient

api_client = stanford.mais.client.MAISClient(...)
sunetids = AccountClient(api_client).only_people()

And, if you only care about active SUNetIDs, you can chain them together, like this:

import stanford.mais.client
from stanford.mais.client.account import AccountClient

api_client = stanford.mais.client.MAISClient(...)
active_sunetids = AccountClient(api_client).only_active().only_people()

Warning

The ‘client’ returned by this method uses the same caches as this client. Therefore, it must not be used across threads/processes.

only_functional() AccountClient[source]

Create a modified AccountClient that can only see functional accounts.

The returned client instance has been modified so that get() only returns functional accounts. If you try to look up a person’s account (a SUNetID), get() will act as if the account does not exist.

See only_people() for examples, tips, and warnings.

get_changed_status(days: int, current_status: Literal['active', 'inactive', 'pending'], get_people: bool = True) frozenset[PartialAccount][source]

Search for accounts which have recently changed status.

Search for all accounts that have changed status within the specified number of days. This is commonly used to get a list of accounts that have changed status

Warning

This call only includes accounts which have changed status. Changes to other attributes—like a person’s name, or a service setting like email aliases—will not cause an account to be included in these results.

Warning

The search results will tell you which accounts changed status, but they will not tell you the old status.

Note

This method is not affected by the modified AccountClient instances returned by only_active(), only_inactive(), only_people(), and only_functional().

Parameters:
  • days – Include accounts that changed status within this many days. Must be at least 1, and at most 30.

  • current_status

    Only accounts with this current status will be included in the results.

    The status pending refers to an account which has just been created and is not yet active. It is only used at the very beginning of an account’s life cycle.

    Note

    Only one current_status value may be provided. If you want a list of all accounts which have changed status, run this search multiple times (once for each status), and perform a set union on the results.

    Warning

    It is possible for an account to change status multiple times during your search period. For example, if you search for current_status active, your search results might include accounts which were already active, went inactive, and then became active again.

  • get_people

    If True (which is the defaut), only accounts for people will be included in the results. If you instead want results for functional accounts, change this to False.

    Note

    The search can return either people accounts or functional accounts, not both. If you want both, then make two searches and perform a set union on the results.

Raises:

When searching for accounts which have changed status recently, the results returned are instances of PartialAccount, representing “lite” account records.

class stanford.mais.account.PartialAccount(sunetid: str, is_person: bool, is_active: bool, last_update: datetime)[source]

Part of a Stanford Account.

This is a “lite” account record. Lite accounts are returned by the Workgroup API in two cases:

  • When you fetch a lite account, instead of a full account (we never do this).

  • When you request a list of accounts that have changed status recently.

The entire contents are read-only.

To get the full account, call the account() method to getch a full account record.

sunetid: str

For people, their SUNetID is also their username. For functional accounts, this is their username. This is the id key from the API.

Tip

This is used as the account’s uid in LDAP.

is_person: bool

If True, this account is for a person. If False, this account is for a “functional account”. This is the type key from the API.

is_active: bool

If True, this account is active. If the account is for a person, then assuming the person’s kerberos service is not frozen or otherwise blocked, they are able to use Stanford Login. If the account is a functional account, then the associated services are active. This does not imply anything else. This is computed from the status key from the API.

last_update: datetime

The timezone-aware datetime when the account was last updated. This is computed from the statusDateStr key from the API.

account(client: AccountClient) Account[source]

Fetch the full Account for this search result.

This is a convenience method, performing a normal account lookup and returning the result.

Warning

The returned Account may have a different is_active or last_update value. Once you call this method, you should stop using this PartialAccount.

Note

The returned Account will have the same is_person value as this partial account.

Parameters:

client – The AccountClient to use for the lookup.

Returns:

The full Account for this PartialAccount’s SUNetID.

Raises:
  • ChildProcessError – Something went wrong on the server side (a 400 or 500 error was returned).

  • KeyError – The SUNetID changed between now and when this PartialAccount was returned in search results. This is extremely rare.

  • PermissionError – You did not use a valid certificate, or do not have permissions to perform the operation. This is also rare, and means you either changed certificates, or your certificate has been disabled or expired.

  • requests.Timeout – The MaIS Workgroup API did not respond in time.

stanford.mais.account.account

class stanford.mais.account.Account(client: AccountClient, sunetid: str, name: str, description: str, is_person: bool, is_active: bool, is_full: bool, services: AccountServiceTypes, last_update: datetime.datetime, raw: dict[str, Any])[source]

A SUNetID Account.

The entire contents are read-only.

Note

Information from the Account API is generally mapped to attributes in the Accounts Tree in Stanford LDAP.

client: AccountClient

The AccountClient representing the API endpoint we are using.

sunetid: str

For people, their SUNetID is also their username. For functional accounts, this is their username. This is the id key from the API.

Tip

This is used as the account’s uid in LDAP.

name: str

For people, this is ther name (last name first). For functional accounts, this is a name set at the time of account creation, followed possibly by a descriptor like “ - shared email”. This is the name key from the API.

description: str

For people, this is a combination of their Org name and their position title. This may be set to “Former …” or the like for inactive accounts For functional accounts, this is a description set at the time of account’s creation. This is the description key from the API.

Tip

This is used as the account’s description in LDAP.

is_person: bool

If True, this account is for a person. If False, this account is for a “functional account”. This is the type key from the API.

is_active: bool

If True, this account is active. If the account is for a person, then assuming the person’s kerberos service is not frozen or otherwise blocked, they are able to use Stanford Login. If the account is a functional account, then the associated services are active. This does not imply anything else. This is computed from the status key from the API.

is_full: bool

If True, this is a person with a a full SUNetID. See SUNet IDs in Detail to learn what it means to have a full SUNetID. We check for full status by seeing if the SUNetID has the leland service associated with it.

If is_full is False but is_person is True, then this is a person with a base SUNetID.

Warning

At this time, is it not possible to tell if the account is sponsored.

services: AccountServiceTypes

This contains the services currently associated with the account. Each service exists as a property, mapping to a subclass of AccountService. If an account does not have a service defined, then the property maps to None.

You can use this technique to see if an account has a service:

def has_kerberos(sunetid: Account) -> bool:
  if sunetid.services.kerberos is None:
    return False
  else:
    return sunetid.services.kerberos.is_active

Note

From time to time, new services are defined. Those services will not appear in this mapping until a software update is released, defining a new subclass for that service. If you need to access the service’s data before that time, use raw, and look in the services key for the new service.

The following services are recognized, and have the following AccountService subclasses:

last_update: datetime.datetime

The timezone-aware datetime when the account was last updated. is already set to the UTC timezone. This is computed from the statusDateStr key from the API.

raw: dict[str, Any]

This is the parsed JSON returned from the MaIS Account API. Most keys have already been parsed, and are available as properties. Here are some additional keys you can find:

  • owner: This is a string with two parts, with a forward-slash used as a separator.

    • If the account is for a person, then this string will be person/ followed by the RegID of the person.

    • If the account is for a functional account, then this string will be organization/ followed by the RegID of the Org which owns the functional account.

  • statusDate: The date when this account was last changed, in the US/Pacific time zone, in the form of a POSIX timestamp tht has been multiplied by 1,000 and microseconds added on to the end. You can parse this using datetime.datetime.fromtimestamp() and the third-party pytz.timezone as follows:

    statusDateInt=account.raw['statusDate']
    statusDate=datetime.datetime.fromtimestamp(
       statusDateInt//(10**3),
       tz=pytz.timezone('US/Pacific')
    ).replace(
       microsecond=statusDateInt%(10**3)
    ).astimezone(
       pytz.utc
    )
    

    Tip

    You really should just use last_update() instead.

  • url: The MaIS Account API URL to look up this account.

classmethod get(client: AccountClient, sunetid: str) Account[source]

Given a string, return an Account.

This uses the MaIS Workgroup API to look up information for an account, using the SUNetID (or functional account username/uid) as input.

This function is memoized; once a lookup is performed, subsequent calls for the same input will return the same result instance, thanks to the use of a cache.

Warning

This will looks up accounts of all types, both accounts for people and also functional accounts. Check is_person before assuming you are working with a SUNetID. Also consider using AccountClient.only_people().

Warning

This memoization means that, should an account change status after lookup, that status change will not be noticed until after the module is reloaded. That means this code should not be used by long-running client code. Use AccountClient.clear_cache() if necessary.

Parameters:
  • client – An AccountClient representing our API endpoint.

  • sunetid – The ID to look up. This must be an actual ID, not an alias.

Raises:
  • ChildProcessError – Something went wrong on the server side (a 400 or 500 error was returned).

  • KeyError – The given ID does not exist. Maybe it was an alias?

  • PermissionError – You did not use a valid certificate, or do not have permissions to perform the operation.

  • UnicodeEncodeError – The ID you provided included non-ASCII characters.

  • ValueError – The input contains non-ASCII characters.

  • NotImplementedError – An unexpected HTTP response was received.

  • requests.Timeout – The MaIS Workgroup API did not respond in time.

class stanford.mais.account.account.AccountServiceTypes(kerberos: AccountServiceKerberos | None, library: AccountServiceLibrary | None, seas: AccountServiceSEAS | None, email: AccountServiceEmail | None, autoreply: AccountServiceAutoreply | None, leland: AccountServiceLeland | None, pts: AccountServicePTS | None, afs: AccountServiceAFS | None, dialin: AccountServiceDialin | None)[source]

The different types of services which may be attached to an Account.

Since not all services may be associated with an account, these are all marked as optional.

stanford.mais.account.service

class stanford.mais.account.service.ServiceStatus(value)[source]

The possible statuses of a service.

ACTIVE = 'active'

The account has and can use this service.

FROZEN = 'frozen'

The account has this service, but it is inaccessible right now.

Note

Not every service uses this status.

INACTIVE = 'inactive'

The account does not have this service.

Note

The account may have had the service in the past, but does not now. For example, full SUNetIDs have access to the autoreply service, but when autoreply is not enabled, the service is inactive.

class stanford.mais.account.service.AccountService(name: str, status: ServiceStatus)[source]

The base class for all account services.

This is a base class representing a service, and stores the common properties that a service can have (the name of the service, and its status). For service-specific properties, check out the documentation for the appropriate subclasses.

name: str

The name of the service.

status: ServiceStatus

The service’s status.

property is_active: bool

True if the service is active.

Danger

This will return True only when the service is active, not when the service is frozen.

For some services, you might not care if the service is frozen. For example, if an account’s kerberos service is frozen, you might want to keep them in the directory, even if you don’t want to allow logins.

property not_inactive: bool

True if the service is not inactive.

This will return True when the service is active or frozen. If the service is inactive, it will return False.

class stanford.mais.account.service.AccountServiceKerberos(name: str, status: ServiceStatus, principal: str, uid: int)[source]

kerberos service for an Account.

This represents an account’s entry in Kerberos. If an account has this service, then the account is at least a base (or base-sponsored) account.

principal: str

The name of the Kerberos principal. This is normally the same as the SUNetID.

Note

This is an un-scoped principal. In other words, it does not contain a Kerberos realm (because Stanford has multiple Kerberos realms!).

uid: int

The UNIX UID number.

Note

This is used as the account’s uidNumber in LDAP.

class stanford.mais.account.service.AccountServiceLibrary(name: str, status: ServiceStatus)[source]

library service for an Account.

New as of 2019, this represents access to Library e-resources. It has no known settings, and you should not assume that having this enabled means the account is full or full-sponsored.

class stanford.mais.account.service.AccountServiceSEAS(name: str, status: ServiceStatus, local: str | None, sunetid: list[str], sunetidpreferred: str, emailsystem: str | None, forward: str | None, urirouteto: str)[source]

seas service for an Account.

The “Stanford Electronic Alias Service”. If this service is active, then the account has an associated @stanford.edu email address, even if they don’t have a Stanford email box.

Note

Even though they include a shared mailbox, Shared Email functional accounts have seas service, but not email service.

local: str | None

This is an optional setting. If the account has a Stanford email box, and the account wants emails delivered to that mailbox, then this is the this is the canonical email address for that mailbox.

Warning

Do not try to send emails directly to this email address.

sunetid: list[str]

This is a setting which may appear multiple times. Each entry represents an @stanford.edu email address. There will always be one entry matching the account’s ID (so that id@stanford.edu works). If the user has any email alises, each alias will appear as an additional entry.

Danger

Do not use this to look up an account’s SUNetID/uid!

sunetidpreferred: str

One of the entries from sunetid, this is the alias the the person prefers others use for email comunication.

Note

This setting, along with the tier-specific suffix (@stanford.edu in PROD), is used for the user’s mail attribute in LDAP (which you can find in the people tree).

emailsystem: str | None

For accounts which have a Stanford electronic mailbox (the email service, this specifies which service hosts said mailbox. Known values include office365 and gmail.

Warning

Functional accounts (including those for Shared Email) do not have the email service, and so they will not have this setting.

forward: str | None

This is an optional setting. If present, emails received by this account will be forwarded to the emails listed in this setting. Multiple emails are separated by a comma.

Warning

Do not try to send emails directly to this email address.

urirouteto: str

When a user points their web browser to to https://stanford.edu/~id, this is the URI where the client will be redirected. If it is just a path (not a full URL), it is relative to https://web.stanford.edu/.

class stanford.mais.account.service.AccountServiceEmail(name: str, status: ServiceStatus, accounttype: str, quota: int | None, admin: str | None)[source]

email service for an Account.

If active, the account has a Stanford electronic mailbox. The seas service should also be present and active.

Note

The specific email backend (Zimbra, Office 365, Google, …) is not indicated.

accounttype: str

For people, this is personal. It should not be used.

quota: int | None

This setting was specific to the Zimbra backend, and should not be used. It may disappear in the future.

admin: str | None

This setting is obsolete, and should not be used. It may disappear in the future.

class stanford.mais.account.service.AccountServiceAutoreply(name: str, status: ServiceStatus, forward: str, subj: str, msg: str)[source]

autoreply service for an Account.

This used to represent the email autoresponder service. If active, incoming emails would have been forwarded to the autoresponder service, which would send an appropriate reply.

In May 2025, the central autoresponder service was turned off. Autoreply is now managed within the user’s email system (Office 365, GMail, etc.). This service may disappear in the future.

forward: str

The account’s canonical email address in the autoresponder system.

Warning

Do not try to send emails directly to this email address.

subj: str

The subject line for the response. The string $SUBJECT, if present, will be replaced with the subject line from the incoming email.

msg: str

The contents of the response. The string $SUBJECT, if present, will be replaced with the subject line from the incoming email. Also, the strings \r and \n represent a carriage return and line feed character, respectively.

class stanford.mais.account.service.AccountServiceLeland(name: str, status: ServiceStatus, shell: str | None)[source]

leland service for an Account.

This represents the Stanford Shared Computing environment, once known as Leland and known today as FarmShare. If active, users are able to log in to FarmShare.

Note

Full and full-sponsored accounts have this active; base and base-sponsored account do not. Functional accounts never have this active.

shell: str | None

The absolute path to the user’s login shell.

Warning

Active accounts that use the default shell do not have this set. The default shell was originally /bin/tcsh, but changed to /bin/bash.

class stanford.mais.account.service.AccountServicePTS(name: str, status: ServiceStatus, uid: int)[source]

pts service for an Account.

This represents an account’s entry in the AFS Protection Server’s database. An account must have an entry here in order to access any AFS services.

uid: int

The UID number for the account in PTS. It should be the same as the account’s UID number in Kerberos.

class stanford.mais.account.service.AccountServiceAFS(name: str, status: ServiceStatus, homedirectory: str)[source]

afs service for an Account.

This represents an account’s AFS home volume.

Note

Just because someone has active AFS service, does not mean they actually have a home volume. New Faculty and Staff members must request an AFS home volume.

Tip

It is still possible to use AFS without a home volume, as long as you use a service (like FarmShare) that does not use AFS for home directories.

homedirectory: str

The path to the account’s home directory.

Note

This is used as the account’s homeDirectory in LDAP. As such, you will probably want to override it.

Note

This setting assumes that AFS is mounted at path /afs on a system. This is normally, but not always, the case. This setting also assumes that your system has an up-to-date copy of the CellServDB file, which should be the case if you are using a packaged OpenAFS client.

class stanford.mais.account.service.AccountServiceDialin(name: str, status: ServiceStatus)[source]

dialin service for an Account.

This represented the Stanford UIT dial-in modem pools, which had various phone numbers, including +1 (415) 498-1440 (this was before area code 650) and +1 (650) 325-1010. If this service was active, you had access to the pool. It has no settings, and should not be used. If may disappear in the future.

stanford.mais.account.validate

This module contains the validate() function, used to bulk-validate a collection of SUNetIDs.

stanford.mais.account.validate.validate(raw: str, client: AccountClient) AccountValidationResults[source]
stanford.mais.account.validate.validate(raw: list[str] | tuple[str] | set[str] | frozenset[str], client: AccountClient) AccountValidationResults
stanford.mais.account.validate.validate(raw: list[str] | tuple[str] | set[str] | frozenset[str], client: AccountClient) AccountValidationResults
stanford.mais.account.validate.validate(raw: list[str] | tuple[str] | set[str] | frozenset[str], client: AccountClient) AccountValidationResults
stanford.mais.account.validate.validate(raw: list[str] | tuple[str] | set[str] | frozenset[str], client: AccountClient) AccountValidationResults

Given a list of SUNetIDs—as a str, list, tuple, set, or frozenset—validate and check status.

This takes a list of SUNetIDs, and returns a list of SUNetIDs which have been checked against the Accounts API for both activeness and service level. The returned result shows which SUNetIDs are active full (or full-sponsored), active base (or base-sponsored), or inactive. All other entries (including those representing functional accounts) are rejected as “unknown”.

If the input is a string, then the input string may be separated by commas, and/or whitespace, (where “whitespace” is a space, newline/linefeed, form feed, carriage return, tab/horizontal tab, or vertical tab character). If the input is a list, tuple, or set; the function assumes that all whitespace etc. have been removed.

Note

“List, tuple, set, or frozenset” is used instead of the generic Collection because a str is also a collection (of one-character str). See typing issue #256 for the discussion around this issue.

This is designed to catch most exceptions. Exceptions related to validation (for example, attempting to validate an obviously-invalid SUNetID like ab$#) will result in the ‘SUNetID’ being added to the unknown list, instead of throwing an exception. The only exceptions that should be expected from this function are ones related to API issues.

Parameters:
  • raw – The list of SUNetIDs. If a str, then the list must be either comma- and/or whitespace-separated.

  • client – An AccountClient to connect to the Account API.

Returns:

The results of the validation. See AccountValidationResults for details.

Raises:
  • ChildProcessError – Something went wrong on the server side (a 400 or 500 error was returned).

  • PermissionError – You did not use a valid certificate, or do not have permissions to perform the operation.

  • requests.Timeout – The MaIS Workgroup API did not respond in time.

Validation results are returned via an instance of AccountValidationResults.

class stanford.mais.account.validate.AccountValidationResults(raw: str | None, raw_set: Collection[str], full: Collection[str], base: Collection[str], inactive: Collection[str], unknown: Collection[str])[source]

Results of doing an account validation.

This class contains the result of calling validate().

raw: str | None

The raw input string provided for validation. This is only provided when a string was provided to validate().

raw_set: Collection[str]

The raw input provided for validation. If a string was provided to validate(), then this is the raw input after being split. If the input to validate() was a set, then this is that set. Otherwise, this is the input to validate(), but as a set.

The set union of full, base, inactive, and unknown is equal to this.

full: Collection[str]

The set of active full (or full-sponsored) SUNetIDs found in raw_set.

base: Collection[str]

The set of active base (or base-sponsored) SUNetIDs found in raw_set.

inactive: Collection[str]

The set of inactive SUNetIDs found in raw_set.

unknown: Collection[str]

The set of entries from raw_set that are not SUNetIDs. This includes uids that are functional accounts.