-
Notifications
You must be signed in to change notification settings - Fork 4
UserManagement
This is a collection of notes about MiG user management for use on existing MiGrid sites hosted at UCPH and but it should apply to any systems using the migrid software. Most facility scripts are available in the mig/server/
directory of the migrid source code checkout, which is typically installed in /home/mig/mig/server
natively or containerized on those systems.
Most commands mentioned here come with a simple built-in help, which can be invoked with the -h
flag.
Please note that some of these commands are also exposed through the Server Admin interface on the site web pages for convenience. Site operators are defined with the admin_list
variable in MiGserver.conf
and can add and use that App on their site pages.
MiGrid includes a createuser.py
script to create MiG users interactively or using saved account requests from the reqcert or reqoid web interfaces. Basically it creates the user in state/user_db_home/MiG-users.db
and initializes all the user-specific sub-directories and files in ~/state/
.
The actual command to use is included in the site operator notification email triggered on account requests and for an openid request could be something like:
~/mig/server/createuser.py -a oid -u '/home/mig/state/user_pending/tmpHfOLef' -p AUTO
to accept the account request saved in tmpHfOLef using automatic detection of Peer acceptance. In the rare cases where no peer is wanted (own test accounts or stand-alone Peer acceptance) the -p
arg can be left out, but for accountability all external users should generally have somebody vouch for them. For certificate requests the command would instead have -a cert
and a third option of -a custom
can be used to specify e.g. mixed auth accounts. The auth type is currently only used to configure/decide individual account expire values for each type.
The operator email will include additional instructions for other relevant actions like informing the user on account creation or deleting the user again at a later point.
Please note that createuser
can also be used to renew an existing user account e.g. because it expired, to change the role field or to add OpenID aliases:
~/mig/server/createuser.py -r -e 1758837600 -R 'external' -o [email protected] -i '/C=SE/ST=NA/L=NA/O=NANO/OU=NA/CN=Alfred Nobel/[email protected]'
The expire value for -e
expects the basic UN*X epoch time format, so it may be more convenient to use an inline expanded actual date like
-e $(date +%s -d 2025-09-26)
MiG fundamentally integrates handling of external users on the site for collaboration and course purposes. Obviously such external users need to be kept in check somehow e.g. with a local management policy always requiring an employee to vouch for such external users. Employees do so either by entering and optionally inviting the external user up front, or by asking the external user to sign up explicitly pointing to her (typically name and organizational email) in the sign up form. When an employee enters a peer or accepts a peer request it triggers a peerupdate email to the site operators to inform them that they can proceed with any pending or incoming account requests from the peer user. The email also contains an expire value used to limit account lifetime and to decide if the account can be renewed without repeated explicit employee acceptance.
If an external user requested an account without previous Peer invite but with a reference to an employee of the local organization who already has an account the site operators can just forward the request to that employee. She will then receive an email linking to her Peers page where she can accept or reject vouching for the user. As an example operators would forward the account request in tmp_wQsEZ to [email protected] with the command:
~/mig/server/reqacceptpeer.py -u '/home/mig/state/user_pending/tmp_wQsEZ' -a -C -I '*[email protected]*'
where the -a
flag specifies that the request should be sent to the registered email address of that employee and the -C
flag to send a copy to the configured site operators.
After acceptance operators could either use the original createuser
command with -p AUTO if the request included the explicit email in the comment, or replace AUTO with a (wildcard) pattern uniquely identifying the employee. In this case the explicit create command was therefore:
~/mig/server/createuser.py -a oid -u '/home/mig/state/user_pending/tmp_wQsEZ' -p '*[email protected]*'
It should be noted that account status is set to temporal when createuser
is used with a peer value or with an explicit expire time so that automatic account expiration gets enforced. More about that in editmeta
below.
Edit an existing user in the MiG-users.db
and in the ~/state/
subdirectories. This is the general version to completely edit even ID-specific fields of a user so that all the state directories and most user files including the ID must be updated. To edit only the user data not part of the actual ID fields it is usually easier to use the lighter editmeta
script, which only changes the MiG-users.db
user entry and is therefore limited to non-ID fields.
To change the email address of a user from [email protected] to [email protected] operators would use:
nice ~/mig/server/edituser.py -i '/C=IT/ST=NA/L=NA/O=Universita di Pisa/OU=NA/CN=Galileo Galilei/[email protected]' \
'Galileo Galilei' 'Universita di Pisa' '' 'IT' '[email protected]'
The format is obviously a bit cumbersome with all ID fields before the changed one needed even if redundant. Please note the quoting which is typically needed to prevent interference from white space.
Edit the metadata of a user in the MiG-users.db
in order to e.g. change the role of a user to Staff
.
~/mig/server/editmeta.py -v '/C=GB/ST=NA/L=NA/O=BIO/OU=NA/CN=Charles Darwin/[email protected]' 'role' 'Staff'
MiGrid sites rely on account status as a central method of controlling account expiry and access. Thus, in case of abuse or similar it is possible to lock an account by changing account status to suspended
or retired
with editmeta
:
~/mig/server/editmeta.py '/C=GB/ST=NA/L=NA/O=PHYS/OU=NA/CN=John Dalton/[email protected]' status suspended
In order to time-limit an existing account the status can be set to temporal
to prevent all automatic renewal
~/mig/server/editmeta.py '/C=GB/ST=NA/L=NA/O=Manchester Academy/OU=NA/CN=Joe Dalton/[email protected]' status temporal
Finally suspended
or temporal
local accounts can be set back to the default active
status in order to again let all web logins proceed and auto-renew the account expire value:
~/mig/server/editmeta.py '/C=GB/ST=NA/L=NA/O=Manchester Academy/OU=NA/CN=Joe Dalton/[email protected]' status active
If the account is for an external user the status should instead be set back to temporal to avoid automatic renewal and enforce automatic expiry.
Please note that account status and expire does NOT cover any associated Seafile accounts. Furthermore on sensitive data sites with GDP-enabled additional measures may be needed.
MiGrid sites additionally include the io_account_expire
configuration option to toggle enforcing expiry for any enabled IO protocol (SFTP, FTPS and WebDAVS) logins as well, to avoid the security implications of stale old accounts. In that way users can only log in through those IO protocols if they have recently logged in on their site web pages or explicitly renewed their account for the configured time frame (a full year if not configured) using repeated sign up.
Sometimes it's convenient to lookup a user in the MiG-users.db
e.g. in relation to support questions. The searchusers.py
script allows searching on specific fields like email:
~/mig/server/searchusers.py -E [email protected]
or on wildcard patterns for the full ID:
~/mig/server/searchusers.py -I '*Niels B*'
Used to clean up completely after no longer active users both in MiG-users.db
and in the ~/state/
subdirectories. This includes all user data so use with care!
To delete a test user for the AtomicTheory1808 workshop we would run:
~/mig/server/deleteuser.py -i '/C=GB/ST=NA/L=NA/O=AtomicTheory1808/OU=NA/CN=John Dalton/[email protected]'
the operation includes a prompt to double-check before deleting.
Use the rejectuser.py
script to reject an external user requesting an account or account renewal e.g. in case no valid access reason or insufficient peer info matching a local employee contact was provided. The rejectuser
script removes the request file and informs the requester that the request was denied and why on email. A link to retry with most fields pre-filled is also included in the email to ease remedying any such insufficient requests.
As an example the command to reject the MATERIALS account renewal request in tmpaEg8V0 due to missing local employee contact details in the request would be:
~/mig/server/rejectuser.py -a oid -C -u '/home/mig/state/user_pending/tmpaEg8V0' -r 'comment field MUST include the name and email of your contact employed locally, as emphasized in the inline help and explained in our FAQ and documentation. HINT: probably John Dalton ([email protected]) for MATERIALS'
Use the notifymigoid.py
script to send out an email with basic login info for users with login through the built-in migoid OpenID 2.0 service. Upon sign up with reqoid the site operator email includes the specific command to create the user and send out the intro email.
As an example the command to inform John Dalton and the site operators about his account access would be:
~/mig/server/notifymigoid.py -a -C -I '/C=GB/ST=NA/L=NA/O=PHYS/OU=NA/CN=John Dalton/[email protected]'
If using a local site identity provider (IDP) like e.g. Active Directory for internal user sign up, say students or employees at a university, they will also manage their passwords through that IDP completely. The MiGrid site will therefore not be involved at all in the password handling but just rely on e.g. an OpenID facility of the IDP to authenticate such users for sign up and access renewal.
For MiGrid sites allowing e.g. collaboration with non-local users the built-in migoid OpenID service can be used for complete user management and authentication. Such external users sign up for an external account and can at any time request a password change through the "Forgot Password?" link found at login page, e.g. https://ext.example.edu if needed. Currently, site operators will have to accept/reject these password renewal requests manually, but it will likely eventually be at least possible to configure this acceptance process to be fully automated.
With automatic expiry on the built-in migoid logins, typically used for external collaboration partner accounts, it is useful to warn users a bit ahead of access expiry. Sites can run the notifyexpire.py
script from cron to do so, say, up to three times starting 30 days before. In that way users get a chance to do semi-automatic renewal with the authenticated Xgi-bin
version of reqoid, where they only need to enter password and comment. After account expiry they either need to enter their account fields again in the form or use the personalized link included in notify emails to auto-fill most of the form fields.
MiGrid sites support mass importing users through the importusers.py
script. It basically takes a list of user IDs and runs the core createuser
actions for each of them. Additionally it allows password auto-generation and integrates support for VGrid/Workgroup membership requests.
Typically someone teaching a course or in charge of a project hands over a list of users on e.g. csv-format. Operators may have to tweak it to some extent but it could look like this:
cat workshop-extra-participants.csv
### full_name;email;organization;country
Ada Lovelace;[email protected];Babbage Research Group;GB
Dorothy Hodgkin;[email protected];University of Cambridge;GB
where the first line is a mandatory header specifying the MiG-users.db
fields and order of the following entries. Site operators can then use the parsecsv.py
script to translate to the internal MiG ID format and the importusers.py
to actually create the users. Please note that it may be needed to juggle with file encodings if the csv comes from Windows or a spreadsheet export. It must be UTF-8 if it contains non-ascii characters. Optional random password generation can be requested with -P AUTO
as shown:
python ~/mig/server/parsecsvusers.py workshop-extra-participants.csv > workshop-extra-participants.list
~/mig/server/importusers.py -v -P AUTO -e $(date --date="2025-09-18 0:00" +'%s') workshop-extra-participants.list
Finally it is usually convenient to send out the account intro email and individual password reminders to inform said users about where they login and what their credentials are.
IFS=$(echo -en "\n\b") ; for i in $(cat workshop-extra-participants.list ); do
~/mig/server/notifymigoid.py -a -C -I "$i" && ~/mig/server/notifypassword.py -a -I "$i"
done
Note the IFS variable which is needed to prevent the shell breaking IDs with space into individual parts.
With automatic account expire operators typically don't need to care too much about removing the accounts again but if needed a similar shell loop can be used for the deleteuser.py
script.
NOTE: in most cases it is now easier to let them use Peers as a self-service solution to import and invite the list of users.
There are a couple of helpers to check user password compliance with the site password policy and to check if 2FA is enabled. To check if David Hume has valid passwords we could run:
~/mig/server/checkpwpolicy.py -I '*Hume*'
Password policy errors:
No password set for /C=GB/ST=NA/L=NA/O=Edinburgh/OU=NA/CN=David Hume/[email protected]
Similarly one can check what 2FA settings for Winter with:
~/mig/server/checktwofactor.py -v -I '*winter@bio*'
Loaded existing user DB from: ./MiG-users.db
2FA status:
Checking /C=GB/ST=NA/L=NA/O=Trinity College/OU=NA/CN=Gregory Winter/[email protected]
inspecting /home/mig/state/user_settings/+C=GB+ST=NA+L=NA+O=Trinity_College+OU=NA+CN=Greogory_Winter+emailAddress=winter@bio.example.edu/twofactor
EXT_OID_TWOFACTOR enabled for /C=GB/ST=NA/L=NA/O=Trinity College/OU=NA/Gregory Winter/[email protected]
MIG_OID_TWOFACTOR not enabled for: /C=GB/ST=NA/L=NA/O=Trinity CollegeNBI/OU=NA/CN=Gregory Winter/[email protected]
WEBDAVS_TWOFACTOR not enabled for: /C=GB/ST=NA/L=NA/O=Trinity College/OU=NA/CN=Gregory Winter/[email protected]
FTPS_TWOFACTOR not enabled for: /C=GB/ST=NA/L=NA/O=Trinity College/OU=NA/CN=Gregory Winter/[email protected]
A custom seed and interval is needed for some TOTP hardware devices. To avoid exposing seeds in shell history and (sudo
) logs operators should read-in the custom seed from a file.
To read a custom seed from file seed.txt
with interval 60 for Gregory run:
~/mig/server/reset2fakey.py -v -i '/C=GB/ST=NA/L=NA/O=Trinity College/OU=NA/CN=Gregory Winter/[email protected]' 'seed.txt' 60
To reset the twofactor key using a random seed and default interval:
~/mig/server/reset2fakey.py -v -i '/C=GB/ST=NA/L=NA/O=Trinity College/OU=NA/CN=Gregory Winter/[email protected]'
Sites can run a simple collection of stat extraction commands weekly with the usagestats.py
script. It looks up disk use, counts users and workgroups, with guesstimates on the number of recent changes and user affiliation.
VGrids/Workgroups are fundamentally user managed and all management of them should really be handled by the relevant owner users. The creator of a VGrid/Workgroup automatically becomes owner and can therefore manage access from corresponding the VGrid/Workgroup administration page. Documentation, data management initiatives and support need to clearly point that user responsibility out to always have users set up one or more co-owners to prevent loss of access in case the original owner leaves.
In emergency cases where all active owners of a VGrid/Workgroup have already left the ownership can still be replaced by operators using add/remove owner commands on the command line.
NOTE The add command checks that it is run on behalf on an existing owner, so the process is always to add new owners as an existing one before removing any old stale ones. Existing owners can be looked up by operators in the state/vgrid_home/VGRIDNAME/owners
file if needed.
The following emulates Geoffrey adding Demis as owner to his alphago VGrid/Workgroup.
cd ~/mig/cgi-bin && \
python fakecgi.py 'addvgridowner.py' POST \
'vgrid_name=alphago&cert_id=/C=GB/ST=NA/L=NA/O=DEEPMIND/OU=NA/CN=Demis Hassabis/[email protected]&rank=1' \
'/C=GB/ST=NA/L=NA/O=DEEPMIND/OU=NA/CN=Geoffrey Hinton/[email protected]' '[email protected]' \
localhost true && cd
the rank
argument can be used to specify the owner ordering in cases where it matters e.g. because the VGrid/Workgroup settings limit certain operations to the first N
owners.
Correspondingly, the following emulates Demis removing Geoffrey as owner of their alphago VGrid/Workgroup.
cd ~/mig/cgi-bin && \
python fakecgi.py 'rmvgridowner.py' POST \
'vgrid_name=alphago&cert_id=/C=GB/ST=NA/L=NA/O=DEEPMIND/OU=NA/CN=Geoffrey Hinton/[email protected]' \
'/C=GB/ST=NA/L=NA/O=DEEPMIND/OU=NA/CN=Demis Hassabis/[email protected]' '[email protected]' \
localhost true && cd
Also note, it is possible to replace vgridowner.py
with vgridmember.py
in both examples above, in order to do similar administration of VGrid/Workgroup members instead of owners.
Removing the last owner of a VGrid/Workgroup results in the VGrid/Workgroup and all the associated data being deleted. The last owner to remove herself is always explicitly prompted to accept that as part of the removal. Operators can also emulate that prompt response by adding a force flag to the command query e.g. by prepending flags=f&
before vgrid_name=...
above.
On high server load the internal VGrid/Workgroup cache may sometimes go out of sync and result in lost admin links for certain VGrid / Workgroup owners (Issue #121). It's a known bug but until it gets fixed the easiest way to fix such issues is to have the owner manually open the admin URL and use the Repair-button at the bottom of that page as described there.
Alternatively the same can be done by a site operator using the corresponding underlying backend command on the site frontend.
In a fictive case for the Quantum workgroup of user /C=DK/ST=NA/L=NA/O=NBI/OU=NA/CN=Niels Bohr/[email protected]
it can be done with the command:
cd ~/mig/cgi-bin && \
python fakecgi.py 'updatevgrid.py' POST 'vgrid_name=Quantum' \
'/C=DK/ST=NA/L=NA/O=NBI/OU=NA/CN=Niels Bohr/[email protected]' \
'[email protected]' localhost true && cd
Afterwards operators can inspect the output of the vgridman.py
backend for the user to verify the link is restored.
cd ~/mig/cgi-bin && \
python fakecgi.py 'vgridman.py' GET 'operation=showlist' \
'/C=DK/ST=NA/L=NA/O=NBI/OU=NA/CN=Niels Bohr/[email protected]' && cd