Accounts API

This is the documentation for the Python client for the MaIS Accounts API. Everything you need is available in the stanford.mais.account.account module.

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

Account API Client

You will use the AccountClient instance to access the API. Once that is done, you can use the get() method to access Accounts.

class stanford.mais.account.AccountClient(client: stanford.mais.client.MAISClient, custom_session: Optional[requests.sessions.Session] = None, _cache: MutableMapping[str, stanford.mais.account.account.Account] = <factory>)[source]

The AccountClient is the first thing you will instantiate when you want to interact with the MaIS Account API. One parameter is required to instantiate a client.

Parameters

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

Once you have a client 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: stanford.mais.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.

session: requests.sessions.Session

The Requests Session to use for API requests.

This session container pre-configures our requests, including client certificate, timeouts, and headers.

Normally, this should not be set, and a new session will be requested from the client. But if you would like to use a custom Session instance (such as for mock testing), provide it to the constructor as the custom_session and it will be used for all requests.

get(sunetid: str)stanford.mais.account.account.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..

only_active()stanford.mais.account.AccountView[source]

Create a modified :class: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()

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()stanford.mais.account.AccountView[source]

Create a modified :class: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.

Warning

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

only_people()stanford.mais.account.AccountView[source]

Create a modified :class: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()stanford.mais.account.AccountView[source]

Create a modified :class: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.

Warning

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

Account

Once you have a Account API client instance, you will be fetching Account instances.

All account attributes will be found within the Account instance. Service-specific information will be found within the services() dict, which will present a different interface depending on the service. Read more about services.

class stanford.mais.account.Account(client: AccountClient, sunetid: str, name: str, description: str, is_person: bool, is_active: bool, is_full: bool, services: stanford.mais.account.account.AccountServiceTypes, last_updated: datetime.datetime, raw: dict)[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

The SUNetID, for people; for functional accounts, the ID. This is the id key from the API.

Note

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. 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 creation. This is the description key from the API.

Note

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 SUNetID is active. In other words, someone could authenticate to Stanford Login using this SUNetID. This does not imply anything else. This is computed from the status key from the API.

is_full: bool

If True, this is a full SUNetID. That means the SUNetID has services like email enabled. A SUNetID can be “full” either by being associated with an active student, faculty, or staff member; or via sponsorship. NOTE: Some services (such as Library e-resources) are not available to all Full SUNetIDs, so this property does not imply access to all services.

services: stanford.mais.account.account.AccountServiceTypes

This contains the services currently associated with the account. Each service has a service name, and the value is a dataclass which contains status and service-specific information.

It is a Mapping of str (the service name) to subclasses of AccountService. To learn the key name for each service, refer to the documentation for that subclass.

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, refer to the services key in the parsed JSON.

last_updated: datetime.datetime

The datetime when the account was last updated. It is timezone-aware, and is already set to the UTC timezone.

raw: dict

This is the parsed JSON returned from the MaIS Accounts 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 (that is, type is “self”), then this string will be person/ followed by the RegID of the person.

    • If the account is for a functional account (type is “functional”), 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
    )
    

    You really should just use last_updated() instead.

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 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.

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.

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.

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

Account Services

Accounts normally have at least one service attached. For example, active accounts have a kerberos service active. Even inactive accounts might have services attached, although those services will all be inactive.

Services are accessed through services(), like so:

aclient = stanford.mais.account.AccountClient(...)
lelandjr = aclient['lelandjr']
if lelandjr.services.kerberos is None:
    lelandjr_uid = None
else:
    lelandjr_uid = lelandjr.services.kerberos.uid

services() acts as a named tuple, with an attribute for each service that the Python MsIS Account API client is aware of. Through these attributes, you can access service status and service-specific settings. For example, the kerberos service maps to the AccountServiceKerberos class.

If an account does not have a service defined, accessing that attribute will return None.

Note

When an account is a functional account, and not an account for a person, there is no guarantee what services will be enabled. For example, a functional account used for a web site’s CGI space will have kerberos, pts, and possibly afs service; a functional account used for shared email will not have any of those, but will have seas service.

Warning

When a service is active, some service-specific settings are guaranteed to be present, and some are optional. However, when a service is not active, all service-specific settings become optional.

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

Represents an account service.

This is a base container representing a service, and stores the common properties that a service can have. For service-specific properties, check out the documentation for the appropriate subclasses.

name: str

The name of the service.

is_active: bool

True if the service is active for the associated account.

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

kerberos service for an Account.

This represents an account’s entry in Kerberos. If active, 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.

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, is_active: bool)[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, is_active: bool, local: str, sunetid: str, sunetidpreferred: str, forward: str, 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

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: 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.

sunetidpreferred: str

This is the alias that the user prefers to use as their ID in their Stanford email address. If the user does not have any aliases, then this will be their account ID.

Note

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

forward: str

This is an optional setting. If present, emails received by this address 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 client browses to https://stanford.edu/~id, this is the URI where the client will be redirected. If it is just a path (and not a full URL), it is relative to https://web.stanford.edu/.

class stanford.mais.account.service.AccountServiceEmail(name: str, is_active: bool, accounttype: str, quota: int, admin: str)[source]

email service for an Account.

This represents a Stanford mailbox. 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

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

admin: str

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

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

autoreply service for an Account.

This represents the email autoresponder service. If active, incoming emails will be forwarded to the autoresponder service, which will send an appropriate reply.

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, is_active: bool, shell: str)[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

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, is_active: bool, 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, is_active: bool, homedirectory: str)[source]

afs service for an Account.

This represents an account’s AFS home directory.

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 ssytem. 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, is_active: bool)[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.

Accounts Validation

The Account class provides all of the tools needed to validate a single SUNetID (that is, determine if it is a valid SUNetID). But if you have a list of SUNetIDs (particularly an unformatted list), the stanford.mais.account.validate module has function that can help.

stanford.mais.account.validate.validate(raw: str, client: stanford.mais.account.AccountClient)stanford.mais.account.validate.AccountValidationResults[source]
stanford.mais.account.validate.validate(raw: Union[List[str], Tuple[str], Set[str]], client: stanford.mais.account.AccountClient)stanford.mais.account.validate.AccountValidationResults
stanford.mais.account.validate.validate(raw: Union[List[str], Tuple[str], Set[str]], client: stanford.mais.account.AccountClient)stanford.mais.account.validate.AccountValidationResults
stanford.mais.account.validate.validate(raw: Union[List[str], Tuple[str], Set[str]], client: stanford.mais.account.AccountClient)stanford.mais.account.validate.AccountValidationResults

Given a list of SUNetIDs; as a string or a list, tuple, or set; 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 activity and service level. The returned result shows which SUNetIDs are active full (or full-sponsored), active base (or base-sponsored), or inactive.

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, or set” is used instead of the generic “collection” because a str is also a collection (of other 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 may be comma- and/or whitespace-separated.

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

Returns

The results of the validation. See the definition of AccountValidationResults for details. If the input to this function is a string; that string will be in raw, and the parsed list will be in raw_set. If the input to this function is not a string, then raw will be None and raw_set will be a copy of the input.

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.

class stanford.mais.account.validate.AccountValidationResults(raw: Optional[str], 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 results of an account validation, called via validate().

Note

Many of these properties are collections. That means, if you want to use things like subscripting, you will want to convert to a more-specific type (such as list before using it).

raw: Optional[str]

The raw input 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 into a collection. Otherwise, this will be the original list/set/tuple that was provided to validate(), but made into a set.

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

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.