openapi: 3.0.0 info: title: ceod - OpenAPI 3.0 description: | This is an OpenAPI specification of ceod, the CSC Electronic Office daemon. Visit the [git repository](https://git.csclub.uwaterloo.ca/public/pyceo) for more details. ## Streaming Responses Many endpoints return a "streaming response", which consists of a series of JSON objects, one per line (the mimetype is text/plain). For example: ``` {"status": "in progress", "operation": "replace_login_shell"} {"status": "in progress", "operation": "replace_forwarding_addresses"} {"status": "completed", "result": "OK"} ``` Whenever an operation is completed, a corresponding JSON object will be streamed from the server to the client. This allows the client to track the server's progress in real time. contact: email: syscom@csclub.uwaterloo.ca version: 1.0.0 servers: - url: https://phosphoric-acid.csclub.uwaterloo.ca:9987/api tags: - name: members description: Operations on members and club reps - name: groups description: Operations on groups and clubs - name: mailman description: Operations on mailing list subscriptions - name: uwldap description: Operations related to the UW LDAP directory - name: database description: Operations related to databases - name: cloud description: Operations related to the CSC Cloud security: - GSSAPIAuth: [] paths: /members: post: tags: ['members'] summary: Create a new user description: >- Creates a new member or club rep. If `terms` is specified, a member is created; if `non_member_terms` is specified, a club rep is created. requestBody: content: application/json: schema: type: object properties: uid: $ref: "#/components/schemas/UID" cn: $ref: "#/components/schemas/UserCN" sn: $ref: "#/components/schemas/UserSN" given_name: $ref: "#/components/schemas/UserGivenName" program: $ref: "#/components/schemas/Program" terms: $ref: "#/components/schemas/Terms" non_member_terms: $ref: "#/components/schemas/NonMemberTerms" forwarding_addresses: $ref: "#/components/schemas/ForwardingAddresses" responses: "200": description: Success content: text/plain: schema: type: string description: Streaming response example: | {"status": "in progress", "operation": "add_user_to_ldap"} {"status": "in progress", "operation": "add_group_to_ldap"} {"status": "in progress", "operation": "add_user_to_kerberos"} {"status": "in progress", "operation": "create_home_dir"} {"status": "in progress", "operation": "send_welcome_message"} {"status": "in progress", "operation": "subscribe_to_mailing_list"} {"status": "in progress", "operation": "announce_new_user"} {"status": "completed", "result": {"cn": "Calum Dalek", "given_name": "Calum", "sn": "Dalek", "uid": "ctdalek", "uid_number": 20001, "gid_number": 20001, "login_shell": "/bin/bash", "home_directory": "/users/ctdalek", "is_club": false, "program": "MAT/Mathematics Computer Science", "terms": ["f2021"], "forwarding_addresses": ["ctdalek@uwaterloo.ca"], "password": "Wlw1wOTofERTEBlXWzR6/MZL"}} /members/{username}: get: tags: ['members'] summary: Get information about a user description: >- Returns information about a member or club rep. The `forwarding_addresses` field will only be present if the client is an authenticated syscom member. parameters: - name: username in: path description: username of the user to return required: true schema: type: string responses: "200": description: Success content: application/json: schema: $ref: "#/components/schemas/User" "404": $ref: "#/components/responses/UserNotFoundErrorResponse" patch: tags: ['members'] summary: Modify a user description: Replace the login shell and/or forwarding addresses of a user parameters: - name: username in: path description: username of the user to modify required: true schema: type: string requestBody: content: application/json: schema: type: object properties: login_shell: $ref: "#/components/schemas/LoginShell" forwarding_addresses: $ref: "#/components/schemas/ForwardingAddresses" responses: "200": description: Success content: text/plain: schema: type: string description: Streaming response example: | {"status": "in progress", "operation": "replace_login_shell"} {"status": "in progress", "operation": "replace_forwarding_addresses"} {"status": "completed", "result": "OK"} "404": $ref: "#/components/responses/UserNotFoundErrorResponse" /members/{username}/renew: post: tags: ['members'] summary: Renew a user description: Add member **or** non-member terms to a user parameters: - name: username in: path description: username of the user to renew required: true schema: type: string requestBody: content: application/json: schema: oneOf: - type: object properties: terms: type: array description: Terms for which this user will be a member items: $ref: "#/components/schemas/Term" - type: object properties: non_member_terms: type: array description: Terms for which this user will be a club rep items: $ref: "#/components/schemas/Term" example: {"terms": ["f2021"]} responses: "200": description: Success content: application/json: schema: oneOf: - type: object properties: terms_added: type: array description: Member terms which were added to this user items: $ref: "#/components/schemas/Term" - type: object properties: non_member_terms_added: type: array description: Non-member terms which were added to this user items: $ref: "#/components/schemas/Term" example: {"terms_added": ["f2021"]} "404": $ref: "#/components/responses/UserNotFoundErrorResponse" /members/{username}/pwreset: post: tags: ['members'] summary: Reset a user's password description: >- Sets a user's password to a randomly generated string, and returns it. The user will be prompted to set a new password on their next login. parameters: - name: username in: path description: username of the user whose password will be reset required: true schema: type: string responses: "200": description: Success content: application/json: schema: type: object properties: password: type: string description: The user's new password example: "EPGbJwLl1pmiWz8Wvu/MSs+v" "404": $ref: "#/components/responses/UserNotFoundErrorResponse" /groups: post: tags: ['groups'] summary: Create a new group description: >- Creates a new Unix group for a club. A new Unix user account with the same name will also be created. A sudo role will be created allowing members of the group to become this user. requestBody: content: application/json: schema: type: object properties: cn: $ref: "#/components/schemas/GroupCN" description: $ref: "#/components/schemas/GroupDescription" responses: "200": description: Success content: text/plain: schema: type: string description: Streaming response example: | {"status": "in progress", "operation": "add_user_to_ldap"} {"status": "in progress", "operation": "add_group_to_ldap"} {"status": "in progress", "operation": "add_sudo_role"} {"status": "in progress", "operation": "create_home_dir"} {"status": "completed", "result": {"cn": "uwclub1", "gid_number": 30001, "description": "Club One", "members": []}} /groups/{group_name}: get: tags: ['groups'] summary: Get information about a group description: Returns information about a group security: [] parameters: - name: group_name in: path description: name of the group to return required: true schema: type: string responses: "200": description: Success content: application/json: schema: type: object properties: cn: $ref: "#/components/schemas/GroupCN" description: $ref: "#/components/schemas/GroupDescription" gid_number: $ref: "#/components/schemas/GIDNumber" members: type: array description: Members of the group items: $ref: "#/components/schemas/UID" "404": $ref: "#/components/responses/GroupNotFoundErrorResponse" /groups/{group_name}/members/{username}: post: tags: ['groups'] summary: Add a member to a group description: >- Adds a member to a group. The member will also be added to any auxiliary groups specified in ceod.conf. The member may also be added to auxiliary mailing lists. parameters: - name: group_name in: path description: name of the group to which the member will be added required: true schema: type: string - name: username in: path description: username of the member who will be added to the group required: true schema: type: string - name: subscribe_to_lists in: query description: whether the member should be subscribed to auxiliary mailing lists schema: type: boolean default: true responses: "200": description: Success content: text/plain: schema: type: string example: | {"status": "in progress", "operation": "add_user_to_group"} {"status": "in progress", "operation": "add_user_to_auxiliary_groups"} {"status": "in progress", "operation": "subscribe_user_to_auxiliary_mailing_lists"} {"status": "completed", "result": {"added_to_groups": ["group2","group3"], "subscribed_to_lists": ["list1","list2"]}} "404": $ref: "#/components/responses/GroupNotFoundErrorResponse" delete: tags: ['groups'] summary: Remove a member from a group description: >- Removes a member from a group. The member will also be removed from any auxiliary groups specified in ceod.conf. The member may also be removed from auxiliary mailing lists. parameters: - name: group_name in: path description: name of the group from which the member will be removed required: true schema: type: string - name: username in: path description: username of the member who will be removed from the group required: true schema: type: string - name: unsubscribe_from_lists in: query description: whether the member should be unsubscribed from auxiliary mailing lists schema: type: boolean default: true responses: "200": description: Success content: text/plain: schema: type: string example: | {"status": "in progress", "operation": "remove_user_from_group"} {"status": "in progress", "operation": "remove_user_from_auxiliary_groups"} {"status": "in progress", "operation": "unsubscribe_user_from_auxiliary_mailing_lists"} {"status": "completed", "result": {"removed_from_groups": ["group2","group3"], "unsubscribed_from_lists": ["list1","list2"]}} "404": $ref: "#/components/responses/GroupNotFoundErrorResponse" /positions: get: tags: ['positions'] summary: Show current positions description: >- Shows the list of positions and members holding them. responses: "200": description: Success content: application/json: schema: type: object additionalProperties: type: string example: president: user0 vice-president: user1 sysadmin: user2 treasurer: post: tags: ['positions'] summary: Update positions description: >- Update members for each positions. Members not specified in the parameters will be removed from the position and unsubscribed from the exec's mailing list. New position holders will be subscribed to the mailing list. requestBody: description: New position holders required: true content: application/json: schema: type: object additionalProperties: type: string example: president: user0 vice-president: user1 sysadmin: user2 treasurer: responses: "200": description: Success content: text/plain: schema: type: string description: Streaming response example: | {"status": "in progress", "operation": "update_positions_ldap"} {"status": "in progress", "operation": "update_exec_group_ldap"} {"status": "in progress", "operation": "subscribe_to_mailing_list"} {"status": "completed", "result": "OK"} "400": description: Failed content: application/json: schema: type: object properties: error: type: string /mailman/{mailing_list}/{username}: post: tags: ['mailman'] servers: - url: "https://mail.csclub.uwaterloo.ca:9987/api" summary: subscribe a user to a mailing list description: Subscribes a user to a mailing list. parameters: - name: mailing_list in: path description: >- The name of the list to which the user will be subscribed. If there is no '@' symbol, then '@csclub.uwaterloo.ca' will be appended to the list name. required: true schema: type: string - name: username in: path required: true description: >- The user who will be subscribed to the list. If there is no '@' symbol, then '@csclub.uwaterloo.ca' will be appended to he username; otherwise, the given email address will be subscribed. schema: type: string responses: "200": description: Success content: application/json: schema: type: object properties: result: type: string example: OK "404": $ref: "#/components/responses/NoSuchListErrorResponse" "409": $ref: "#/components/responses/UserAlreadySubscribedErrorResponse" delete: tags: ['mailman'] servers: - url: "https://mail.csclub.uwaterloo.ca:9987/api" summary: unsubscribe a user from a mailing list description: Unsubscribes a user from a mailing list. parameters: - name: mailing_list in: path description: >- The name of the list from which the user will be unsubscribed. If there is no '@' symbol, then '@csclub.uwaterloo.ca' will be appended to the list name. required: true schema: type: string - name: username in: path required: true description: >- The user who will be unsubscribed from the list. If there is no '@' symbol, then '@csclub.uwaterloo.ca' will be appended to he username; otherwise, the given email address will be unsubscribed. schema: type: string responses: "200": description: Success content: application/json: schema: type: object properties: result: type: string example: OK "404": $ref: "#/components/responses/UserAlreadySubscribedOrNoSuchListErrorResponse" /uwldap/{username}: get: tags: ['uwldap'] security: [] summary: get UWLDAP information for a user description: Returns information about a user from the UW LDAP directory. parameters: - name: username in: path description: username of the user to return required: true schema: type: string responses: "200": description: Success content: application/json: schema: $ref: "#/components/schemas/UWLDAPUser" "404": $ref: "#/components/responses/UserNotFoundErrorResponse" /uwldap/updateprograms: post: tags: ['uwldap'] summary: update CSC programs from UWLDAP description: | Sync the 'program' attribute in the CSC LDAP with the UW LDAP. The JSON request body may be omitted. parameters: - name: dry_run in: query description: >- Whether to perform a dry run or not. If true, a list of members who *would* have been changed is returned. schema: type: boolean default: false requestBody: content: application/json: schema: type: object properties: members: type: array description: If non-empty, only these members will be synced with UWLDAP default: [] items: $ref: "#/components/schemas/UID" responses: "200": description: Success content: application/json: schema: type: array description: >- A list of members whose programs were (or who would have been) changed. Each item of the list is a 3-tuple of the form (username, old_program, new_program). items: type: array items: type: string example: [['ctdalek', 'old_program', 'new_program']] /db/mysql/{username}: post: tags: ['database'] servers: - url: https://caffeine.csclub.uwaterloo.ca:9987/api summary: Create a MySQL database description: Create a MySQL database for the user parameters: - name: username in: path description: name of the user to create MySQL database for required: true schema: type: string responses: "200": description: Success content: application/json: schema: type: object properties: password: type: string description: The password to the database example: {"password": "7fUi5rQr/lcpeEj4M86ZBbwM"} "400": $ref: "#/components/responses/InvalidUsernameErrorResponse" "404": $ref: "#/components/responses/UserNotFoundErrorResponse" "409": $ref: "#/components/responses/DBAlreadyCreatedErrorResponse" "500": $ref: "#/components/responses/DBConnectionOrPermissionErrorResponse" /db/mysql/{username}/pwreset: post: tags: ['database'] servers: - url: https://caffeine.csclub.uwaterloo.ca:9987/api summary: Reset MySQL database password description: Reset the password for a user's MySQL database parameters: - name: username in: path description: name of the user to reset the MySQL database password for required: true schema: type: string responses: "200": description: Success content: application/json: schema: type: object properties: password: type: string description: The new password to the database example: {"password": "kM90d3G/eofIUxr9O3CQHTlP"} "400": $ref: "#/components/responses/InvalidUsernameErrorResponse" "404": $ref: "#/components/responses/UserNotFoundErrorResponse" "500": $ref: "#/components/responses/DBConnectionOrPermissionErrorResponse" /db/postgresql/{username}: post: tags: ['database'] servers: - url: https://caffeine.csclub.uwaterloo.ca:9987/api summary: Create a PostgreSQL database description: Create a PostgreSQL database for the user parameters: - name: username in: path description: name of the user to create PostgreSQL database for required: true schema: type: string responses: "200": description: Success content: application/json: schema: type: object properties: password: type: string description: The password to the database example: {"password": "iHxd62DC7Qt1HyYRj18P6ujS"} "400": $ref: "#/components/responses/InvalidUsernameErrorResponse" "404": $ref: "#/components/responses/UserNotFoundErrorResponse" "409": $ref: "#/components/responses/DBAlreadyCreatedErrorResponse" "500": $ref: "#/components/responses/DBConnectionOrPermissionErrorResponse" /db/postgresql/{username}/pwreset: post: tags: ['database'] servers: - url: https://caffeine.csclub.uwaterloo.ca:9987/api summary: Reset PostgreSQL database password description: Reset the password for a user's PostgreSQL database parameters: - name: username in: path description: name of the user to reset the PostgreSQL database password for required: true schema: type: string responses: "200": description: Success content: application/json: schema: type: object properties: password: type: string description: The new password to the database example: {"password": "CdPHT49iYAWzzKuhKTt2dNeu"} "400": $ref: "#/components/responses/InvalidUsernameErrorResponse" "404": $ref: "#/components/responses/UserNotFoundErrorResponse" "500": $ref: "#/components/responses/DBConnectionOrPermissionErrorResponse" /cloud/accounts/create: post: tags: ['cloud'] servers: - url: https://biloba.csclub.uwaterloo.ca:9987/api summary: Activate a cloud account description: Activate a cloud account for the calling user responses: "200": "$ref": "#/components/responses/SimpleSuccessResponse" "403": "$ref": "#/components/responses/InvalidMembershipErrorResponse" /cloud/accounts/purge: post: tags: ['cloud'] servers: - url: https://biloba.csclub.uwaterloo.ca:9987/api summary: Purge expired accounts description: | Delete the cloud accounts of expired members. There is a one-month grace period after the expiration. After one month, expired members will be sent an email warning them that their account will be deleted. One week after that, if an expired member has still not renewed their membership, their account will be deleted. responses: "200": description: Success content: application/json: schema: type: object properties: accounts_to_be_deleted: type: array description: usernames of accounts which will be deleted items: type: string accounts_deleted: type: array description: usernames of accounts which were deleted items: type: string /cloud/vhosts/{domain}: put: tags: ['cloud'] servers: - url: https://biloba.csclub.uwaterloo.ca:9987/api summary: Create a vhost description: Add a new virtual host configuration. parameters: - name: domain in: path description: domain name of the virtual host required: true schema: type: string requestBody: content: application/json: schema: type: object properties: ip_address: type: string description: IP address of the virtual host example: {"ip_address": "172.19.134.11"} responses: "200": "$ref": "#/components/responses/SimpleSuccessResponse" "403": "$ref": "#/components/responses/InvalidMembershipErrorResponse" delete: tags: ['cloud'] servers: - url: https://biloba.csclub.uwaterloo.ca:9987/api summary: Delete a vhost description: Delete a virtual host configuration. parameters: - name: domain in: path description: domain name of the virtual host required: true schema: type: string responses: "200": "$ref": "#/components/responses/SimpleSuccessResponse" /cloud/vhosts: get: tags: ['cloud'] servers: - url: https://biloba.csclub.uwaterloo.ca:9987/api summary: List all vhosts description: List all virtual host configurations for the calling user. responses: "200": description: Success content: application/json: schema: type: object properties: vhosts: type: array description: virtual hosts items: type: object properties: domain: type: string description: domain name of the virtual host ip_address: type: string description: IP address of the virtual host example: {"vhosts": [{"domain": "ctdalek.m.csclub.cloud", "ip_address": "172.19.134.11"}]} components: securitySchemes: GSSAPIAuth: type: http scheme: negotiate description: | ceod uses SPNEGO-based authentication over HTTP, as specified in [RFC 4559](https://datatracker.ietf.org/doc/html/rfc4559). It is basically a base64-encoded Kerberos ticket wrapped in a HTTP header. For endpoints which make modifications to LDAP, the Delegate flag must also be set. For example, with cURL: ```sh kinit curl --negotiate -u : --service-name ceod --delegation always -X POST -d '{"terms":["w2022"]}' https://phosphoric-acid.csclub.uwaterloo.ca/api/members/ctdalek/renew ``` schemas: UserCN: type: string description: Full name example: Calum Dalek UserSN: type: string description: Last name example: Dalek UserGivenName: type: string description: First name example: Calum UID: type: string description: Username example: ctdalek Program: type: string description: Academic program example: MAT/Mathematics Computer Science Terms: type: array description: Terms for which this user was a member items: $ref: "#/components/schemas/Term" NonMemberTerms: type: array description: Terms for which this user was a club rep items: $ref: "#/components/schemas/Term" LoginShell: type: string description: Login shell example: /bin/bash UIDNumber: type: integer description: UID number example: 20001 GIDNumber: type: integer description: GID number example: 20001 User: type: object properties: cn: $ref: "#/components/schemas/UserCN" sn: $ref: "#/components/schemas/UserSN" given_name: $ref: "#/components/schemas/UserGivenName" uid: $ref: "#/components/schemas/UID" uid_number: $ref: "#/components/schemas/UIDNumber" gid_number: $ref: "#/components/schemas/GIDNumber" home_directory: type: string description: Home directory example: /users/ctdalek is_club: type: boolean description: Whether this user is a club account or not example: false login_shell: $ref: "#/components/schemas/LoginShell" program: $ref: "#/components/schemas/Program" positions: type: array description: Positions held by this member items: type: string example: president terms: $ref: "#/components/schemas/Terms" non_member_terms: $ref: "#/components/schemas/NonMemberTerms" forwarding_addresses: $ref: "#/components/schemas/ForwardingAddresses" UWLDAPUser: type: object properties: uid: $ref: "#/components/schemas/UID" cn: $ref: "#/components/schemas/UserCN" given_name: type: string description: Given name example: Calum sn: type: string description: Surname example: Dalek mail_local_addresses: type: array description: UW email addresses for this user items: type: string format: email example: ctdalek@uwaterloo.ca ForwardingAddresses: type: array description: Forwarding addresses in ~/.forward items: type: string format: email example: ctdalek@uwaterloo.ca Term: type: string description: Academic term example: f2021 GroupCN: type: string description: the name of the group example: uwclub1 GroupDescription: type: string description: a description of the group example: Club One responses: ErrorResponse: &ErrorResponse content: application/json: schema: type: object properties: error: type: string description: Error message UserNotFoundErrorResponse: <<: *ErrorResponse description: User not found GroupNotFoundErrorResponse: <<: *ErrorResponse description: Group not found UserAlreadySubscribedErrorResponse: <<: *ErrorResponse description: User is already subscribed NoSuchListErrorResponse: <<: *ErrorResponse description: Mailing list does not exist UserAlreadySubscribedOrNoSuchListErrorResponse: <<: *ErrorResponse description: User is already subscribed or mailing list does not exist DBAlreadyCreatedErrorResponse: <<: *ErrorResponse description: User already has a database InvalidUsernameErrorResponse: <<: *ErrorResponse description: Username contains invalid characters DBConnectionOrPermissionErrorResponse: <<: *ErrorResponse description: Unable to connect to database or action failed due to permissions InvalidMembershipErrorResponse: <<: *ErrorResponse description: Membership is invalid or expired SimpleSuccessResponse: description: Success content: application/json: schema: type: object properties: status: type: string description: '"OK"' example: {"status": "OK"}