Workgroup API
This is the documentation for the Python client for the MaIS Workgroup API. Everything you need is available here.
To begin using the MaIS Workgroup API, you should first instantiate a
MAISClient. Once that is done, you can
use it to instantiate a WorkgroupClient.
Workgroup API Client
You will use the WorkgroupClient instance to
access the Workgroup API. Instantiating a
WorkgroupClient is easy:
client = MAISClient(...)
wclient = WorkgroupClient(client)
For performance, the WorkgroupClient
maintains a cache of fetched workgroups. To clear the cache, call
clear_cache().
Warning
Once you clear the cache, avoid using any Workgroup instances that you might have used before!
Fetching Workgroups
Once you have a WorkgroupClient, there are
several ways you can access workgroups. The workgroups you access will be
instances of the Workgroup class.
One way is to use the get()
method to fetch a workgroup, providing the name of the workgroup that you want.
Important
Workgroup names are all lowercase.
The
WorkgroupClient also implements the
__getitem__ method, so instead of calling
get() you can get workgroups as
you would items from a dict.
Here is an example of the two ways you can get a workgroup.
wclient = WorkgroupClient(...)
# These two operations give you the same Workgroup:
nero_users = wclient.get('nero:users')
nero_users = wclient['nero:users']
Searching for Workgroups
Workgroup Existence
If you know the name of the workgroup you are looking for, the
WorkgroupClient implements
Container functionality, so you can use the client to
check if a workgroup exists:
wclient = WorkgroupClient(...)
nero_admins = (wclient['nero:admins'] if 'nero:admins' in wclient else None)
Searching by Name
Workgroups can be searched for by using a “starts with” -style search with wildcards. Search results do not include all the details of the workgroups, so if you want full information, you need to make a call to fetch the full details of the workgroup:
wclient = WorkgroupClient(...)
nero_workgroup_results = wclient.search_by_name('nero:*')
nero_workgroups = list(result.workgroup() for result in nero_workgroup_results)
Although it’s common to put a single asterisk at the end of the search—for a “starts with” type of search—you can put wildcards in the middle of a search:
wclient = WorkgroupClient(...)
owner_workgroup_results = wclient.search_by_name('workgroup:*-owners')
owner_workgroups = list(result.workgroup() for result in owner_workgroup_results)
The search results — instances of class
PartialWorkgroup — include the workgroup
name and description, which might be enough for you:
wclient = WorkgroupClient(...)
nero_workgroup_results = wclient.search_by_name('nero:*')
print("The nero stem has " + len(nero_workgroup_results) + " workgroups:")
for nero_workgroup in nero_workgroup_results:
print(f" {nero_workgroup.name}: {nero_workgroup.description}")
Searching by Membership
Besides searching for workgroups by name, you can also search for workgroups
based on who (or what) is included in a workgroup.
search_by_user() searches for
workgroups by SUNetID, returning workgroups containing the user as a direct
or indirect member or administrator.
Important
This search may give unexpected results!
For example, say a user is a member of workgroup test:a, and test:a is nested into the administrators of test:b. Here is what you would see when running a search:
wclient = WorkgroupClient(...)
results = wclient.search_by_user('user')
a_in_results = any(wg.name == 'test:a' for wg in s.is_member) # True
b_in_results = any(wg.name == 'test:b' for wg in s.is_administrator) # Also True!
If you want to want to check if the user is directly in the workgroup, you have to look up the workgroup and check. For example:
wclient = WorkgroupClient(...)
results = wclient.search_by_user('user')
b_in_results = any(wg.name == 'test:b' for wg in s.is_administrator) # True
wg = wclient['test:b'] # Membership checks need the full workgroup
user_in_b = ('user' in wg.administrators.people) # False!
a_in_b = ('test:a' in wg.administrators.workgroups) # True!
wga = wclient['test:a'] # Fetch test:a to check
user_in_a = ('user' in wga.members.people) # True!
In the above example, user is an administrator of test:b, but only because user is a member of test:a, and all members of test:a are administrators of test:b.
In addition to searching for workgroups containing a person, you can search for
workgroups containing a workgroup (using
search_by_workgroup()), and you
can search for workgroups containing a certificate (using
search_by_certificate()). The
behavior of
search_by_workgroup() and
search_by_certificate() is the
same as search_by_user().
Note
search_by_workgroup() is
special: In addition to accepting a workgroup name as a string, you can
provide a Workgroup or
PartialWorkgroup as the search parameter,
and the name will be extracted.
Creating Workgroups
Creating workgroups can be done by calling
create(). The only required
parameters are a name and description.
wclient = WorkgroupClient(...)
wg = wclient.create('test:a', 'My Test Workgroup')
When you only provide required parameters to
create(), the following defaults
are used:
filter: noneprivgroup: enabledreusable: yesvisibility: Stanford
If you want to change these, you may do so changing the properties of the created workgroup instance, or you can provide them during creation, like so:
wclient = WorkgroupClient(...)
wg = wclient.create(
name='test:a',
description='My Test Workgroup',
filter=WorkgroupFilter.ACADEMIC_ADMINISTRATIVE,
privgroup=True,
reusable=True,
visibility=WorkgroupVisibility.PRIVATE
)
Note
Members and administrators can only be added after the workgroup is created.
Your newly-created workgroup will not have any members, but it will have two administrators:
The stem-owner workgroup will be an administrator. For example, workgroup test:a will have workgroup:test-owners as an administrator. This cannot be changed or removed.
The certificate which created the workgroup will be an administrator. This can be removed.
Workgroup Class
You use a Workgroup instance to interact with
your workgroup.
Fetch, Create, Refresh, & Delete
The WorkgroupClient can be used to fetch and
create workgroups, but Workgroup also
provides classmethods to do the same:
wclient = WorkgroupClient(...)
# These three operations give you the same Workgroup:
nero_users = wclient.get('nero:users')
nero_users = wclient['nero:users']
nero_users = Workgroup.get(client=wclient, name='nero:users')
# You can also use a classmethod to create a workgroup:
Workgroup.create(
client=wclient,
name='nero:sysadmins',
description='Nero sysadmins',
)
Once a Workgroup instance has been created
(through get(),
create(), etc.), any attempts to
get() the same workgroup will return
an instance from the cache. So, to update the data in your instance, run the
refresh() method.
Important
Once you call refresh(), you must
be prepared for anything to change (except the workgroup name). It’s even
possible for the workgroup to be deleted!
As for deleting a workgroup, that is accomplished using the
delete() method.
wclient = WorkgroupClient(...)
nero_users = wclient.get('nero:users')
nero_users.delete()
Deleting a workgroup is immediate, but not permanent: Deleted workgroups are
simply made ‘inactive’. Should you every try fetching or modifying a deleted
workgroup, a WorkgroupDeleted exception will
be raised.
Deleted workgroups may be un-deleted by a stem owner, through the Workgroup Manager web site.
Properties
Every Workgroup has properties, some of which
are read-only and some of which may be modified (assuming you have
permissions).
Note
This section provides summaries only. See the Module Documentation for details about each property!
Read-only Properties
Four (read-only) properties can be read even if the workgroup has been deleted:
name: The workgroup’s name.deleted: True if the workgroup has been deleted; False otherwise.client: This returns theWorkgroupClientwhich was used to fetch the workgroup.last_refresh: This is the last time that the full workgroup was fetched from the API, which happens when callingrefresh()or when changing one of the properties.Important
Changing a workgroup’s members or administrators does not trigger a refresh.
Two properties are read-only, and can only be read if the workgroup is not deleted:
last_update: Thedatetime.datewhen the workgroup (not theWorkgroupinstance, the actual underlying workgroup) was last changed.Warning
This date is in the Stanford-local time zone. Remember to take this into account when doing comparisons.
can_see_membership: True if your client certificate is able to see a workgroup’s membership; False otherwise.
Read-write Properties
These properties are read-write, and can only be read if the workgroup is not deleted.
Warning
These properties may only be changed if your client certificate is an administrator of the workgroup, either directly or via stem ownership.
description: The workgroup’s description, limited to 255 Latin1-encoded characters.filter: The affiliations of people who are allowed to be members of the workgroup.privgroup: True if this workgroup has a privgroup.reusable: True if this workgroup may be nested into workgroups outside the same stem. (Workgroups may always be nested within the same stem.)visibility: The visibility of this workgroup’s members and administrators.
Workgroup Membership
Within your Workgroup instance, you can
access (and modify) the workgroup’s members and administrators using the
members and
administrators properties.
Note
members and
administrators are almost
identical in how they work, so this guide will focus on
members only. When there is a
behavior difference between
members and
administrators, the difference
will be mentioned.
Warning
The members and
administrators properties should
only be retained for a short amount of time. They are closely tied to their
associated Workgroup.
To get the total number of members, use len:
wclient = WorkgroupClient(...)
nero_users = wclient.get('nero:users')
number_of_members = len(nero_users.members)
number_of_members == (
len(nero_users.members.people) +
len(nero_users.members.workgroups) +
len(nero_users.members.certificates)
) # True
When you access a workgroup’s membership through
members, you will receive an
instance which has three properties:
people: The people who are members of the workgroup. Each set member is a SUNetID.workgroups: The nested workgroups. Each set member is a workgroup name.certificates: The certificates who are members of the workgroup. Each set member is a certificate CN (Common Name).Note
In almost all cases, certificates may only be administrators of workgroups, not members. The exception is that certificates may be stem owners, so they are allowed to be members of stem-owner
workgroup:*-ownersworkgroups.
people,
workgroups, and
certificates are
all sets of strings: You may get the set’s length, iterate over the sets
members, add set members, remove set members, etc.. Each set will raise an
exception if you attempt to add an inappropriate type of string (for example,
attempting to add a workgroup to
people).
Here is an example of how to remove a person from membership of all workgroups in a stem:
wclient = WorkgroupClient(...)
person_to_remove = 'jsmith'
test_workgroup_results = wclient.search_by_name('test:*')
for test_workgroup_result in test_workgroup_results:
test_workgroup = test_workgroup_result.workgroup()
test_workgroup.members.people.discard(person_to_remove)
Since the discard() method only removes from the set if the
element is in the set (in other words, it ignores KeyError), we do not
need to check for set membership first.
Warning
This is a simplified example, which skips all error checking. Be sure to read the Module Documentation before using this code for production work!
Privgroup
A workgroup’s membership is a collection of people, workgroups, and certificates, but downstream systems are normally only able to think in terms of people: Downstream systems don’t care if a person is a member of workgroup:x directly or indirectly, they just care if a person is a member. So, downstream systems do not normally think about workgroups, they think about privgroups.
A privgroup is a list of SUNetIDs. A workgroup has two privgroups: One for
members and one for administrators. You can fetch both lists by calling
get_privgroup().
For example, you can use
get_privgroup() to get the names of
everyone who is a member of a workgroup:
wclient = WorkgroupClient(...)
nero_users_workgroup = wclient.search_by_name('nero:users')
nero_users_privgroup = nero_users_workgroup.get_privgroup()
with open('nero_usernames.txt', mode='w', encoding='ascii') as f:
for nero_user in nero_users_privgroup.members:
print(nero_user.sunetid, file=f)
get_privgroup() is the better option
for this case, because nero_users_workgroup.members.people would only
return people who are direct members of the workgroup. Indirect
members—people who are members because they are members of a nested
workgroup—are not included in nero_users_workgroup.members.people.
A workgroup only has a privgroup if the workgroup’s
privgroup property is True.
Privgroups are created by starting with all of the members (or administrators)
who are people. If a workgroup is nested, then the nested workgroup’s
privgroup is calculated and included. Finally, filters are applied. When
constructing a privgroup, all certificates are ignored.
Important
The steps for generating a privgroup listing is complicated. For details,
see the Module Documentation of
get_privgroup().
Module Documentation
Here is detailed documentation for all Workgroup modules.
stanford.mais.workgroup
WorkgroupClient represents your Workgroup API
client.
- class stanford.mais.workgroup.WorkgroupClient(client: ~stanford.mais.client.MAISClient, _cache: ~collections.abc.MutableMapping[str, ~weakref.ReferenceType[~stanford.mais.workgroup.workgroup.Workgroup]] = <factory>)[source]
The
WorkgroupClientis the first thing you will instantiate when you want to interact with the MaIS Workgroup 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 a workgroup. For your convenience, instances of this class also implement__getitem__, so instead of doing…wclient = WorkgroupClient(...) nero_users = wclient.get('nero:users')
… you can do …
wclient = WorkgroupClient(...) nero_users = wclient['nero:users']
Instances also implement
Containerfunctionality, so you can check for workgroup existence like so:wclient = WorkgroupClient(...) nero_users_exists = (True if 'nero:users' in wclient else False)
Through the use of caching, if you then decide to fetch the workgroup after confirming its existance, the entry will be served from cache instead of making a fresh API request.
Note
Take care in how you use this code, given that workgroups are cached. Should you be concerned about the accuracy of a cached
Workgroup, feel free toWorkgroup.refresh()it.Each instance provides the following attributes:
- client: MAISClient
A
MAISClientinstance.This configures the API endpoint (accessed via
client.urls['workgroup']) and client key/cert to use. It must be provided when calling the class constructor.
- create(*args, **kwargs) Workgroup[source]
Create a Workgroup.
This is a convenience wrapper around
Workgroup.create(). All other parameters provided are passed through tocreate(), and the resulting instance is returned.Note
When calling
Workgroup.create(), this convenience method will provide theclientparameter for you. You are responsible for providing all other parameters.Refer to
Workgroup.create()for details on parameters, exceptions, etc..
- get(name: str) Workgroup[source]
Fetch a Workgroup.
This is a convenience wrapper around
Workgroup.get(). The workgroup name you provide is passed through toget(), and the resulting instance is returned.Note
If the workgroup you are requesting is already available in the cache, the cached instance will be returned instead.
You can also use dict-style key access, like so:
wclient = WorkgroupClient(...) nero_users = wclient['nero:users']
Refer to
Workgroup.get()for details on exceptions, etc..
- search_by_name(search: str) Collection[PartialWorkgroup][source]
Search for workgroups by name, with wildcards supported.
- Parameters:
search –
The string to search for.
*is the wildcard symbol.To limit your search to a specific stem, provide the stem name and colon before the first wildcard. For example, to list all workgroups in stem
abc, search forabc:*. To search for all Research Computing sysadmin workgroups, search forresearch-computing:sysadmins*.To search across all stems, you may omit the
*. However, you must provide at least for characters before the first wildcard. For exaple,mais*will work, butmai*will fail.You may have multiple wildcards in your search.
- Returns:
A collection of partial workgroups.
- Raises:
ChildProcessError – Something went wrong on the server side (a 400 or 500 error was returned). This exception is also raised if you are doing a not-stem-limited search, and you do not have at least 4 characters before the first wildcard.
KeyError – The workgroup does not exist.
PermissionError – You did not use a valid certificate, or do not have permissions to perform the operation.
ValueError – The search string began with an asterisk, or was empty.
ValueError – The input contains non-ASCII characters.
NotImplementedError – Received an unexpected HTTP response code.
requests.Timeout – The MaIS Workgroup API did not respond in time.
- search_by_user(sunetid: str) SearchByResults[source]
Search for workgroups containing the specified SUNetID.
A workgroup will be included in the result if the specified SUNetID is a member or an administrator (or both).
Note
If the user is a stem administrator, then the user automatically becomes an administrator of every workgroup in that stem, and all those workgroups will be included in the results.
- Parameters:
sunetid –
The SUNetID to search for.
Note
Wildcards are not allowed.
- Returns:
A set of partial workgroups where the person is a member, and a set of partial workgroups where the person is an administrator.
- Raises:
ChildProcessError – Something went wrong on the server side (a 400 or 500 error was returned). Or, you searched for a SUNetID that does not exist.
PermissionError – You did not use a valid certificate, or do not have permissions to perform the operation.
ValueError – The search string is empty.
requests.Timeout – The MaIS Workgroup API did not respond in time.
- search_by_certificate(certificate: str) SearchByResults[source]
Search for workgroups containing the specified certificate.
A workgroup will be included in the result if the specified certificate is an administrator of the workgroup, or is a stem administrator.
Note
If the certificate is a stem administrator, then the certificate automatically becomes an administrator of every workgroup in that stem, and all those workgroups will be included in the results.
- Parameters:
certificate –
The “common name” of the certificate to search for. The common name is also known as the “CN” attribute, and is part of the certificate’s subject.
Note
Wildcards are not allowed.
- Returns:
A set of partial workgroups where the certificate is a member, and a set of partial workgroups where the person is an administrator.
- Raises:
ChildProcessError – Something went wrong on the server side (a 400 or 500 error was returned). Or, you searched for a certificate that does not exist.
PermissionError – You did not use a valid certificate, or do not have permissions to perform the operation.
ValueError – The search string is empty.
requests.Timeout – The MaIS Workgroup API did not respond in time.
- search_by_workgroup(workgroup: str) SearchByResults[source]
Search for workgroups containing the specified workgroup.
A workgroup will be included in the result if the specified workgroup is nested in the workgroup’s members or administrators list.
For example, say you are searching for workgroup
abc:def. If workgroupabc:123hasabc:defnested in the members or administrators lists (or both), then workgroupabc:123will be included in the results.Note
If the workgroup is a stem administrator, then the workgroup automatically becomes an administrator of every workgroup in that stem, and all those workgroups will be included in the results.
- Parameters:
workgroup –
The workgroup to search for.
Note
This must be a fully-qualified workgroup name. For example,
research-computing:sysadmins. Wildcards are not allowed.- Returns:
A set of partial workgroups where the workgroup is nested as a member, and a set of partial workgroups where the workgroup is nested as an administrator.
- Raises:
ChildProcessError – Something went wrong on the server side (a 400 or 500 error was returned). Or, you searched for a workgroup that does not exist.
PermissionError – You did not use a valid certificate, or do not have permissions to perform the operation.
ValueError – The search string is empty.
requests.Timeout – The MaIS Workgroup API did not respond in time.
- clear_cache() None[source]
Clear cache of workgroups.
This clears the cache of workgroups.
As mentioned in the class docs, visited workgroups are cached locally, for speed and to reduce load on the Workgroup API. In long-running programs, this can be a problem. To assist, this method clears the cache of this specific Workgroup client.
Note
If you are looking to see changes in a specific workgroup, you should not use this method. Instead, you should call
refresh()on the workgroup of interest.Danger
If you are holding a reference to an existing
Workgroup, or to one of the the Workgroup’sWorkgroupMembershiporWorkgroupMembershipContainer, clearing the cache does not invalidate those references!This method should not be called unless you know what you are doing.
Searches for workgroups never return full
Workgroup instances, they return
PartialWorkgroup instances.
- class stanford.mais.workgroup.PartialWorkgroup(name: str, description: str, last_update: ~datetime.date, as_of: ~datetime.datetime = <factory>)[source]
Part of a Stanford Workgroup.
This contains the most basic parts of a workgroup. It is what is returned whenever someone does a search for a workgroup.
Warning
Instances of this class are only accurate at the time of the instance’s creation!
- last_update: date
The date when the workgroup was last updated.
Warning
This date is in the Stanford-local time zone. Remember to take this into account when doing comparisons.
Note
Why is it a date, instead of a datetime? Because that is what the Workgroup API provides.
- workgroup(client: WorkgroupClient) Workgroup[source]
Return the full
Workgroupobject.This returns the full
Workgroupobject for this PartialWorkgroup. It is a convenience wrapper around theWorkgroupClient.get()method.
When doing a “search by” -type search, the results are separated into “search
target is an administrator” and “search target is a member”. The actual return
type is SearchByResults.
- class stanford.mais.workgroup.SearchByResults(is_member: frozenset[stanford.mais.workgroup.PartialWorkgroup], is_administrator: frozenset[stanford.mais.workgroup.PartialWorkgroup])[source]
- is_member: frozenset[PartialWorkgroup]
Partial workgroups where the search target is a member.
- is_administrator: frozenset[PartialWorkgroup]
Partial workgroups where the search target is an administrator.
stanford.mais.workgroup.workgroup
This module defines our Workgroup class, and
some related classes.
- class stanford.mais.workgroup.Workgroup(**kwargs)[source]
A Stanford Workgroup.
- classmethod create(client: WorkgroupClient, name: str, description: str, filter: WorkgroupFilter = WorkgroupFilter.NONE, privgroup: bool = True, reusable: bool = True, visibility: WorkgroupVisibility = WorkgroupVisibility.STANFORD) Workgroup[source]
Create a new workgroup.
Your client certificate must be a stem owner in order to create workgroups in a given stem.
Once created, a workgroup will have no members, and two administrators:
The stem-owner group. For example, workgroup
abc:defwill haveworkgroup:abc-ownersnested as an administrator. This cannot be changed.Your client certificate. Even though your client certificate is already a stem owner—and therefore an administrator—it will be explicitly added as an administrator. You are allowed to remove your client certificate from the list of workgroup administrators. To do so, use code like this:
wclient = WorkgroupClient(...) workgroup = Workgroup.create(client=wclient, ...) workgroup.administrators.certificates.remove(CLIENT_CERT_CN)
- Parameters:
client – A
WorkgroupClient.name – The fully-qualified workgroup name. Required. For example, to create a workgroup named abc in stem school, use name school:abc. The name portion is limited to 81 characters, which may contain only lowercase letters, numbers, hyphens, and underscores. The name portion must start with a letter or number.
description – The workgroup description. It must contain at least one character. Limited to 255 characters from the ISO 8859-1 (“Latin 1”) character set.
filter – Optional. See
filter(). Defaults to NONE.privgroup – Optional. See
privgroup(). Defaults to True.reusable – Optional. See
reusable(). Defaults to True.visibility – Optional. See
visibility(). Defaults to STANFORD.
- Returns:
An instance of the newly-created workgroup.
- Raises:
ChildProcessError – Something went wrong on the server side (a 400 or 500 error was returned).
IndexError – The proposed workgroup name or description is too short or too long.
ValueError – The proposed workgroup name or description has invalid characters.
KeyError – A workgroup with this name already exists.
The proposed workgroup name was already used for a workgroup, and that workgroup has since been deleted.
Note
Deleted workgroups are not really deleted, just hidden. Stem owners can restore ‘deleted’ workgroups through the Workgroup Manager web site.
PermissionError – You did not use a valid certificate, or do not have permissions to perform the operation.
NotImplementedError – Received an unexpected HTTP response code.
requests.Timeout – The MaIS Workgroup API did not respond in time.
- classmethod get(client: WorkgroupClient, name: str) Workgroup[source]
Fetch a Workgroup.
Fetch an existing Workgroup using the MaIS Workgroup API, and return the corresponding
Workgroupinstance. If the instance already exists in the cache, the cached instance will be returned.Note
Use the
last_refreshproperty to see if your instance is too old, and therefresh()method to refresh it.Warning
If the workgroup’s visibility is set to PRIVATE, and your client certificate is not an administrator of the workgroup (either directly, or via stem ownership), then…
The
can_see_membershipproperty will beFalse;The sets of members and administrators will be empty; and
All attempts to access the privgroup list or make changes will return a
PermissionError.
- Parameters:
client – A
WorkgroupClientrepresenting our API endpoint.name – The name of the workgroup to fetch.
- Raises:
ChildProcessError – Something went wrong on the server side (a 400 or 500 error was returned), or you did not provide a workgroup name.
KeyError – The workgroup does not exist, and has never existed.
WorkgroupDeleted – The workgroup used to exist, but was deleted.
PermissionError – You did not use a valid certificate.
NotImplementedError – Received an unexpected HTTP response code.
requests.Timeout – The MaIS Workgroup API did not respond in time.
- refresh() None[source]
Refresh an existing Workgroup instance.
Make a query to the Workgroups API to update this instance. This will refresh all properties, including membership. The only thing guaranteed not to change is the workgroup’s name.
Note
It is possible that your client certificate gained administrator access between this instance’s creation, and now. It is also possible that your client certificate lost administrator access.
Danger
It is also possible that someone else has deleted the workgroup. If that happens, the
deletedproperty will be set and aWorkgroupDeletedexception will be raised.Warning
If your client certificate is not an administrator of the workgroup (either directly, or via stem ownership), then…
The
can_see_membershipproperty will beFalse;The sets of members and administrators will be empty; and
All attempts to access the privgroup list or make changes will return a
PermissionError.
- 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.
WorkgroupDeleted – The workgroup has been deleted.
NotImplementedError – Received an unexpected HTTP response code.
requests.Timeout – The MaIS Workgroup API did not respond in time.
- property client: WorkgroupClient
Return the
WorkgroupClientthat was used to fetch thisWorkgroup.
- property deleted: bool
Is the workgroup deleted?
If
delete()is called on the workgroup, this property is set toTrue, and all methods & properties will raise anEOFError. The exception are this method, andclient.Note
This property may also become
Trueif you calledrefresh()on the instance, and the refresh reveals that the workgroup has been deleted.
- property name: str
The fully-qualified name of the workgroup.
This may not be changed after the workgroup is created.
Note
Since workgroups are never actually “deleted”, just hidden, the name remains accessible even after deletion.
- property last_refresh: datetime
When the instance was last refreshed.
This is set to the (aware) datetime when the instance was last refreshed from the Workgroup API. The following actions can trigger a refresh:
Note
If
delete()is called to delete the workgroup, this will return the datetime when the workgroup was deleted.
- property last_update: date
The date when the workgroup was last updated.
When the workgroup’s properties, members, or administrators are updated, this datestamp resets.
Warning
This date is in the Stanford-local time zone. Remember to take this into account when doing comparisons.
Note
Why is it a date, instead of a datetime? Because that is what the Workgroup API provides.
Danger
Privgroup member and administrator changes do not reset the last_update datestamp. This is a “last updated” for the workgroup, not the privgroup!
- Raises:
EOFError – The workgroup has been deleted.
- property can_see_membership: bool
Can we see the workgroup’s members and administrators?
See
visibilityfor an explanation of workgroup visibility.If this workgroup’s visibility is STANFORD, then we can see the members and administrators of the workgroup.
If this workgroup’s visibility is PRIVATE, then the client certificate must be a workgroup administrator. If yes, then we can see the members and administrators of the workgroup.
If this workgroup’s visibility is PRIVATE, and the client certificate is not a workgroup administrator, then we cannot see the members and administrators of a workgroup: The sets will appear empty.
- Raises:
EOFError – The workgroup has been deleted.
- property members: WorkgroupMembership
Access the sets of workgroup members.
Note
This is the real set of members, not the effective membership. Refer to
WorkgroupFilterfor more information.See
WorkgroupMembershipfor information on how access the sets of member people (SUNetIDs), workgroups (fully-qualified names), and certificates (client certificate subject common names).- Raises:
EOFError – The workgroup has been deleted.
- property administrators: WorkgroupMembership
Access the sets of workgroup administrators.
Note
There is no difference between the real and effective set of workgroup administrators. Anyone or anything included here has administrative power over the workgroup, regardless of filter. The one exception is when a nested workgroup has a filter set, in which case the filter applies to that workgroup’s membership only.
Refer to
WorkgroupFilterfor more information on filters.See
WorkgroupMembershipfor information on how access the sets of administrator people (SUNetIDs), workgroups (fully-qualified names), and certificates (client certificate subject common names).- Raises:
EOFError – The workgroup has been deleted.
- get_privgroup() PrivgroupContents[source]
Generate the current privilege group listing.
A privgroup is a list of actual people (no workgroups or certificates). A workgroup has two privgroups: One for members and one for administrators.
For the privgroup of members, start with an empty list, and do the following:
Add all of this workgroup’s members that are people.
For each of this workgroup’s members that are workgroups, if the nested workgroup’s privgroup property is enabled, then calculate the nested workgroup’s privgroup (members only), and add those people to this list.
Remove all duplicates
Remove all people who do not meet this workgroup’s filter.
For the privgroup of administrators, start with an empty list, and do the following:
Add all of this workgroup’s administrators that are people.
For each of this workgroup’s administrators that are workgroups, if the nested workgroup’s privgroup property is enabled, then calculate the nested workgroup’s privgroup (members only), and add those people to this list.
Remove all duplicates
Remove all people who do not meet this workgroup’s filter.
Since the privgroup’s contents potentially depend on ths membership of other workgroups, and may also depend on the affiliation of each member, the workgroup’s privgroup must be fetched when it is needed.
Note
To get the privgroup for a workgroup, your client certificate must be able to see the membership of this workgroup, otherwise a PermissionError will be raised. See
can_see_membership().Warning
It is possible that your client certificate gained administrator access between this instance’s creation, and now. If you think that happened, call
refresh()before calling this method.It is also possible that your client certificate lost administrator access, or that a workgroup was made private. In that case, this method will return a PermissionError even though
can_see_membership()returns True.Danger
It is also possible that someone else has deleted the workgroup. If that happens, the
deletedproperty will be set and aWorkgroupDeletedexception will be raised.- 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.
WorkgroupDeleted – The workgroup has been deleted.
NotImplementedError – Received an unexpected HTTP response code.
requests.Timeout – The MaIS Workgroup API did not respond in time.
- property description: str
The workgroup description.
This is a property: Use as a value to get the current description; call with a string to change the current setting.
Warning
Changing a workgroup property triggers a refresh of the entire workgroup. See the documentation for
refresh(); all of those warnings apply here.- Parameters:
value (str) – The new description. The maximum length is 255 characters.
- 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.
IndexError – The new description is too long.
EOFError – The workgroup has been deleted.
- property filter: WorkgroupFilter
The workgroup filter.
This controls the effective membership of the workgroup. See the definition of
WorkgroupFilterfor more information on how Workgroup filters take the real membership (which you see through this API) and filter it to provude an effective membership (which others see).This is a property: Use as a value to get the current description; call with a
WorkgroupFilter(or equivalent string) to change the current setting.Warning
Changing a workgroup property triggers a refresh of the entire workgroup. See the documentation for
refresh(); all of those warnings apply here.- Parameters:
value – The new filter. This may either be a
WorkgroupFilter, or a string that parses cleanly into aWorkgroupFilter.- 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.
ValueError – The value provided does not match an enum value. This may only be thrown when providing a
strfor value.EOFError – The workgroup has been deleted.
- property privgroup: bool
Does the workgroup have an associated privgroup?
A ‘privgroup’ is a flattened list of workgroup members and administrators, with certificates removed, nested workgroups flattened, and filters applied.
As described in
filter(), workgroups have a real list of members (which is accessed & maintained by this API) and an effective list of members (after the workgroup membership is flattened and filter applied).If privgroup is
True, then the flattened and filtered list of workgroup members and administrators will be made available to downstream systems like LDAP. IfFalse, then the workgroup may only be used within Workgroup Manager (for example, it can be nested in other workgroups).Warning
If you disable privgroup on a workgroup and then nest it into another workgroup, this workgroup’s members will not appear in the privgroup of the nested workgroup.
Note
This property cannot be changed through the Workgroup Manager web site, only through the API.
This is a property: Use as a value to get the current description; call with a
boolto change the current setting.Warning
Changing a workgroup property triggers a refresh of the entire workgroup. See the documentation for
refresh(); all of those warnings apply here.- Parameters:
value (bool) – The new privgroup setting.
- 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.
EOFError – The workgroup has been deleted.
- property reusable: bool
May the workgroup be nested outside of its stem?
If reusable is
True, then this workgroup may only be nested within other workgroups of the same stem. Otherwise, this workgroup may be nested in any workgroup.“nesting” means including the effective membership of one workgroup in another. See
filter()for information on real vs. effective workgroup membership.Use as a property to get the current reusable setting; call with parameters to change the current reusable setting.
Warning
Changing this setting will not affect existing nesting relationships. You can view existing nesting relationships in Workgroup Manager.
This is a property: Use as a value to get the current description; call with a
boolto change the current setting.Warning
Changing a workgroup property triggers a refresh of the entire workgroup. See the documentation for
refresh(); all of those warnings apply here.- Parameters:
value (bool) – The new reusable setting.
- 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.
EOFError – The workgroup has been deleted.
- property visibility: WorkgroupVisibility
Is the workgroup’s membership visible to others?
See
WorkgroupVisibilityfor an explanation of the different workgroup visibility options.Use as a property to get the current visibility setting; call with parameters to change the current filter setting.
Danger
Setting this to
PRIVATEcan cause unexpected and unusual issues in downstream applications.This is a property: Use as a value to get the current description; call with a
WorkgroupVisibility(or equivalent string) to change the current setting.Warning
Changing a workgroup property triggers a refresh of the entire workgroup. See the documentation for
refresh(); all of those warnings apply here.- Parameters:
value – The new visibility. This may either be a
WorkgroupVisibility, or a string that parses cleanly into aWorkgroupVisibility.- 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.
ValueError – The value provided does not match an enum value.
EOFError – The workgroup has been deleted.
- delete() None[source]
Delete the workgroup.
You must be an administrator in order to delete it.
Warning
Once a workgroup has been deleted, you may not create a new workgroup with the same name. Stem owners may restore deleted workgroups through the Workgroup Manager web site.
Note
It is possible that the workgroup was already deleted by someone else. If that happens, we will update the instance accordingly, and then raise a
WorkgroupDeletedexception. If you don’t care about this case, catch the exception and then continue as normal.- 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.
WorkgroupDeleted – The workgroup has been deleted unexpectedly.
NotImplementedError – Received an unexpected HTTP response code.
requests.Timeout – The MaIS Workgroup API did not respond in time.
- static datestr_to_date(datestr: str) date[source]
Convert a Workgroups API date-string to a DateTime Date
- Parameters:
datestr – A string in the form “01-Jan-2020”
- Returns:
A DateTime Date.
- Raises:
ValueError – The provided string could not be parsed
When fetching a workgroup’s privgroup, the return type is a
PrivgroupContents, with properties for
accessing a privgroup’s members and a privgroup’s administrators.
- class stanford.mais.workgroup.PrivgroupContents(members: 'set[PrivgroupEntry]', administrators: 'set[PrivgroupEntry]')[source]
Each property is a PrivgroupEntry.
- class stanford.mais.workgroup.PrivgroupEntry(sunetid: 'str', name: 'str', last_update: 'datetime.date')[source]
Finally, WorkgroupDeleted is an
exception which will be raised when trying to access or change a workgroup that
has been deleted.
- class stanford.mais.workgroup.WorkgroupDeleted[source]
Bases:
KeyErrorWorkgroup used to exist, but has been deleted.
This exception is raised whenever a workgroup is “inactive”, which means that it used to exist, but has been deleted. Constrast that with a workgroup never having existed, which triggers the raise of a KeyError.
This exception exists for folks who care if a workgroup used to exist: Folks who don’t care can simply catch KeyError, which will work because this is a subclass of KeyError.
stanford.mais.workgroup.properties
There are two Workgroup properties which are
not text fields or booleans. Those two properties are represented as enums,
and are defined in this module.
WorkgroupFilter is used to control
the effective membership of a workgroup.
- class stanford.mais.workgroup.properties.WorkgroupFilter(value)[source]
Options for filtering Workgroup membership.
By default, any active SUNetID may be a workgroup member or administrator. With filters, it is possible to change that.
If you set a filter on a workgroup, the effective membership (which downstream consumers will see) is determined using the following process:
Flatten the workgroup. For every workgroup that has been nested into this workgroup, run this flattening/filtering process on that workgroup, and then add the resulting membership to this workgroup.
Using the now-flatted list, evaluate each member against the selected filter. If the workgroup member does not match the selected filter, remove them.
The resulting list is the effective workgroup membership that others see.
The resulting list is used by downstream workgroups (that this workgroup is nested in), and is also used when you fetch the privgroup for the workgroup.
If you use this API to add someone who does not match the filter, the action will complete successfully. Whenever a person changes status, all related workgroups have their memberships re-computed automatically. For example, if you add a non-student to a staff-only workgroup, and they become staff the next day, they will suddenly become “in” the workgroup.
Note
When you use
membersandadministrators, you are accessing the real lists. To see the effective lists, useget_privgroup().Note
Once a SUNetID goes inactive (for example, by a student having graduated), the SUNetID is removed from the workgroup—both the real and effective lists—shortly after the SUNetID goes inactive. This cleanup mechanism is not affected by filters. If you have questions, or do not want it to apply to your workgroup stem, contact MaIS.
Note
Normally, calling
stron anEnumwill result in a fully-qualified string (for example,WorkgroupFilter.NONE). If you callstrhere, only the value (for example,NONE) will be returned.- NONE = 'NONE'
No membership filters are set.
- ACADEMIC_ADMINISTRATIVE = 'ACADEMIC_ADMINISTRATIVE'
Limit effective membership to faculty, staff, students, and sponsored SUNetIDs.
- STUDENT = 'STUDENT'
Limit effective membership to students.
- FACULTY = 'FACULTY'
Limit effective membership to faculty.
- STAFF = 'STAFF'
Limit effective membership to staff.
- FACULTY_STAFF = 'FACULTY_STAFF'
Limit effective membership to faculty & staff.
- FACULTY_STUDENT = 'FACULTY_STUDENT'
Limit effective membership to faculty & students.
- STAFF_STUDENT = 'STAFF_STUDENT'
Limit effective membership to staff & students.
- FACULTY_STAFF_STUDENT = 'FACULTY_STAFF_STUDENT'
Limit effective membership to faculty, staff, and students.
This is similar to, but more restrictive than,
ACADEMIC_ADMINISTRATIVE.
- classmethod from_str(value: str) WorkgroupFilter[source]
Convert a string into an enum.
- Parameters:
visibility – The string to convert. Is case-sensitive.
- Raises:
ValueError – The value provided does not match an enum value.
WorkgroupVisibility is used to
control who can see a workgroup’s members & administrators.
- class stanford.mais.workgroup.properties.WorkgroupVisibility(value)[source]
Options for workgroup membership visibility.
Workgroup administrators may control the visibility of workgroup’s membership. The different options available are defined here.
Note
Normally, calling
stron anEnumwill result in a fully-qualified string (for example,WorkgroupVisibility.PRIVATE). If you callstrhere, only the value (for example,PRIVATE) will be returned.- STANFORD = 'STANFORD'
The workgroup’s lists of members and administrators are visibile to all authenticated users.
- PRIVATE = 'PRIVATE'
The workgroup’s lists of members and administrators are only visibile to workgroup administrators. To everyone else, the workgroup will appear empty.
Danger
This limitation applies within Workgroup Manager and the Workgroup API, and it applies to some downstream applications, but not all!
For example, LDAP receives member and administrator information for all workgroups, including private workgroups. However, Stanford Login does not. So, private workgroups may not be used for authentication.
- classmethod from_str(visibility: str) WorkgroupVisibility[source]
Convert a string into an enum.
- Parameters:
visibility – The string to convert. It is case-sensitive.
- Raises:
ValueError – The value provided does not match an enum value.
stanford.mais.workgroup.members
Every workgroup contains two collections of containers, one for members —
accessed through the members
property — and one for administrators — accessed through the
administrators property. The
code implementing those collections exists in this module.
Each collection of containers is an instance of
WorkgroupMembership.
- class stanford.mais.workgroup.member.WorkgroupMembership(workgroup: Workgroup, collection_type: Literal['members', 'administrators'])[source]
Holds the sets of workgroup members or administrators.
Each
Workgroupincludes two instances of this class, accessible through the following properties:members()provides access to the sets of workgroup members. As a reminder, this the real list of workgroup members, without filtering. SeeWorkgroupFilterfor details.administrators()provides access to the sets of workgroup administrators.
Within this class, the members (or administrators) are divided into three categories, each of which has its own property accessor:
peopleprovides access to the set of members who are people. The values stored are SUNetIDs, and the type isWorkgroupMembershipPersonContainer.workgroupsprovides access to the set of nested workgroups. The values stored are fully-qualified workgroup names (in the typical ‘stem:name’ form), and the type isWorkgroupMembershipWorkgroupContainer.certificatesprovides access to the set of certificates. The values stored are certificate subject common names (CNs), and the type isWorkgroupMembershipCertificateContainer.
Warning
If the workgroup’s visibility is set to
PRIVATE, and you are not an administrator of the workgroup, then all workgroup member and administrator sets will appear empty.Finally, this class supports using
len()to find out the total number of workgroup members (or administrators).- property people: WorkgroupMembershipPersonContainer
Access the list of workgroup members that are people.
- property workgroups: WorkgroupMembershipWorkgroupContainer
Access the list of nested workgroups.
- property certificates: WorkgroupMembershipCertificateContainer
Access the list of workgroup members that are certificates.
- update_from_upstream(response_json: list[Mapping[str, Any]]) None[source]
Report new membership details from upstream.
This is used to update an instance with a new list of members or administrators, as received from the Workgroups API. This is the only way to update the instance that does not trigger calls to upstream.
- Parameters:
response_json – Either the members or administrators part of the JSON returned by the Workgroup API.
- Raises:
EOFError – The related Workgroup instance no longer exists.
Each collection has three containers: One container of people, one container of
workgroups, and one container of certificates. The base class for containers
is WorkgroupMembershipContainer.
- class stanford.mais.workgroup.member.WorkgroupMembershipContainer(workgroup: Workgroup, collection_type: Literal['members', 'administrators'])[source]
A container for workgroup members: People, Certificates, or Workgroups. Instances of this class are used to hold one type of workgroup member. Either all people, all certificates, or all workgroups.
This container acts like a
set, and so all of the normal set methods (likelen()andadd()) are supported. in is supported to check for membership.Warning
Changes made here result in API calls, changing the Workgroup membership. Be very careful before you do things like calling
clear().Note
Changes made here happen synchronously within the Workgroups API, but asynchronously elsewhere. In other words, when calls like
add()return successfully, you know that the change has been made and takes effect immediately within Workgroup Manager, but it will take time for the change to propagate to the privgroup, and to downstream clients like LDAP.- add(value: str) None[source]
Add a new identifier to the container.
This triggers an API call to add the identifier to the workgroup.
Note
It is possible that your client certificate gained administrator access between this instance’s creation, and now. It is also possible that your client certificate lost administrator access.
Danger
It is also possible that someone else has deleted the workgroup. If that happens, the
deletedproperty will be set and aWorkgroupDeletedexception will be raised.- Raises:
ChildProcessError – Something went wrong on the server side (a 400 or 500 error was returned).
EOFError – The related Workgroup instance no longer exists.
KeyError – The identifier was already added.
WorkgroupDeleted – The workgroup has been deleted.
PermissionError – You did not use a valid certificate, or do not have permissions to perform the operation.
ValueError – Workgroup Manager does not know about that identifier. For example, you tried adding a SUNetID that does not exist.
NotImplementedError – Received an unexpected HTTP response code.
requests.Timeout – The MaIS Workgroup API did not respond in time.
- discard(value: str) None[source]
Remove an identifier from the container.
This triggers an API call to remove the identifier from the workgroup.
Note
As per the set definition, this method does not return KeyError if you attempt to discard an element that is not in the set. If you want a
KeyErrorto be raised, use the remove method.Note
It is possible that your client certificate gained administrator access between this instance’s creation, and now. It is also possible that your client certificate lost administrator access.
Danger
It is also possible that someone else has deleted the workgroup. If that happens, the
deletedproperty will be set and aWorkgroupDeletedexception will be raised.- Raises:
ChildProcessError – Something went wrong on the server side (a 400 or 500 error was returned).
EOFError – The related Workgroup instance no longer exists.
WorkgroupDeleted – The workgroup has been deleted.
PermissionError – You did not use a valid certificate, or do not have permissions to perform the operation.
NotImplementedError – Received an unexpected HTTP response code.
requests.Timeout – The MaIS Workgroup API did not respond in time.
The container of people is represented by the
WorkgroupMembershipPersonContainer.
- class stanford.mais.workgroup.member.WorkgroupMembershipPersonContainer(workgroup: Workgroup, collection_type: Literal['members', 'administrators'])[source]
A read-write container of people.
Note
If your client certificate is not a workgroup administrator, then the container will be read-only.
This is a set of strings, where each string is a SUNetID. Attempting to add something which is not a SUNetID will raise a ValueError.
Note
At this time, instead of a ValueError, a ChildProcessError will be raised. TODO: Report this issue to MaIS.
Warning
The Workgroups API only supports SUNetIDs. It does not support SUNet aliases. For example, akkornel is a SUNetID (acceptable), and karl.kornel is a SUNet alias (not acceptable).
The container of workgroups is represented by the
WorkgroupMembershipWorkgroupContainer.
- class stanford.mais.workgroup.member.WorkgroupMembershipWorkgroupContainer(workgroup: Workgroup, collection_type: Literal['members', 'administrators'])[source]
A read-write container of workgroups.
Note
If your client certificate is not a workgroup administrator, then the container will be read-only.
This is a set of strings, where each string is a workgroup name in full stem:name form. For example,
research-computing:sysadminsis a fully-qualified workgroup name.For convenience, the following methods accept a
WorkgrouporPartialWorkgroupinstance, in addition to a string:__contains__()(also known asin)
Note
Regardless of what you
add()—str,WorkgrouporPartialWorkgroup— the set will only ever contain (and return) strings.Attempting to add something which is not a workgroup will raise a ValueError.
The container of certificates is represented by the
WorkgroupMembershipCertificateContainer.
- class stanford.mais.workgroup.member.WorkgroupMembershipCertificateContainer(workgroup: Workgroup, collection_type: Literal['members', 'administrators'])[source]
A read-write container of certificates.
Note
If your client certificate is not a workgroup administrator, then the container will be read-only.
Certificates are identified by their “common name”. This is the CN part of the certificate’s Subject.
Attempting to add something which is not a certificate will raise a ValueError.
Note
In almost all cases, certificates may only be workgroup administrators. Attempting to add a certificate as a workgroup member will raise a ChildProcessError.