From 769785c2992e0985922fca148e84ee2cd4a05144 Mon Sep 17 00:00:00 2001
From: Justin Chung <20733699+justin13888@users.noreply.github.com>
Date: Thu, 13 Oct 2022 17:52:01 -0400
Subject: [PATCH 1/5] Implement TUI support for multiple users in each position
---
ceod/api/positions.py | 10 +++++++---
.../members/UpdateMemberPositionsTransaction.py | 13 ++++++++++---
2 files changed, 17 insertions(+), 6 deletions(-)
diff --git a/ceod/api/positions.py b/ceod/api/positions.py
index a194565..b23af20 100644
--- a/ceod/api/positions.py
+++ b/ceod/api/positions.py
@@ -15,7 +15,10 @@ def get_positions():
positions = {}
for user in ldap_srv.get_users_with_positions():
for position in user.positions:
- positions[position] = user.uid
+ if position in positions:
+ positions[position] += f", {user.uid}"
+ else:
+ positions[position] = user.uid
return positions
@@ -29,9 +32,10 @@ def update_positions():
required = cfg.get('positions_required')
available = cfg.get('positions_available')
- # remove falsy values
+ # remove falsy values and parse multiple users in each position
+ # Example: "user1,user2, user3" -> ["user1","user2","user3"]
body = {
- positions: username for positions, username in body.items()
+ positions: username.replace(' ','').split(',') for positions, username in body.items()
if username
}
diff --git a/ceod/transactions/members/UpdateMemberPositionsTransaction.py b/ceod/transactions/members/UpdateMemberPositionsTransaction.py
index 32ac77e..56fff2d 100644
--- a/ceod/transactions/members/UpdateMemberPositionsTransaction.py
+++ b/ceod/transactions/members/UpdateMemberPositionsTransaction.py
@@ -2,6 +2,7 @@ from collections import defaultdict
from typing import Dict
from zope import component
+from typing import Union
from ..AbstractTransaction import AbstractTransaction
from ceo_common.interfaces import ILDAPService, IConfig, IUser
@@ -20,7 +21,7 @@ class UpdateMemberPositionsTransaction(AbstractTransaction):
'subscribe_to_mailing_lists',
]
- def __init__(self, positions_reversed: Dict[str, str]):
+ def __init__(self, positions_reversed: Dict[str, Union[str,list]]):
# positions_reversed is position -> username
super().__init__()
self.ldap_srv = component.getUtility(ILDAPService)
@@ -28,8 +29,14 @@ class UpdateMemberPositionsTransaction(AbstractTransaction):
# Reverse the dict so it's easier to use (username -> positions)
self.positions = defaultdict(list)
for position, username in positions_reversed.items():
- self.positions[username].append(position)
-
+ if isinstance(username, str):
+ self.positions[username].append(position)
+ elif isinstance(username, list):
+ for user in username:
+ self.positions[user].append(position)
+ else:
+ raise TypeError("Username(s) under each position must either be a string or a list")
+
# a cached Dict of the Users who need to be modified (username -> User)
self.users: Dict[str, IUser] = {}
--
2.39.5
From d16e7bb293cdfa730673b486d8b9c4969b4cbe72 Mon Sep 17 00:00:00 2001
From: Justin Chung <20733699+justin13888@users.noreply.github.com>
Date: Thu, 13 Oct 2022 20:17:48 -0400
Subject: [PATCH 2/5] Fixed linting
---
ceo/tui/controllers/SetPositionsController.py | 5 +++++
ceod/api/positions.py | 2 +-
.../transactions/members/UpdateMemberPositionsTransaction.py | 4 ++--
3 files changed, 8 insertions(+), 3 deletions(-)
diff --git a/ceo/tui/controllers/SetPositionsController.py b/ceo/tui/controllers/SetPositionsController.py
index 17f8000..a3937ff 100644
--- a/ceo/tui/controllers/SetPositionsController.py
+++ b/ceo/tui/controllers/SetPositionsController.py
@@ -8,6 +8,10 @@ import ceo.tui.utils as tui_utils
from ceo.tui.views import TransactionView
from ceod.transactions.members import UpdateMemberPositionsTransaction
+from ceo_common.logger_factory import logger_factory
+
+logger = logger_factory(__name__)
+
class SetPositionsController(Controller):
def __init__(self, model, app):
@@ -18,6 +22,7 @@ class SetPositionsController(Controller):
for pos, field in self.view.position_fields.items():
if field.edit_text != '':
body[pos] = field.edit_text
+ logger.info(body)
model = TransactionModel(
UpdateMemberPositionsTransaction.operations,
'POST', '/api/positions', json=body
diff --git a/ceod/api/positions.py b/ceod/api/positions.py
index b23af20..7bf7243 100644
--- a/ceod/api/positions.py
+++ b/ceod/api/positions.py
@@ -35,7 +35,7 @@ def update_positions():
# remove falsy values and parse multiple users in each position
# Example: "user1,user2, user3" -> ["user1","user2","user3"]
body = {
- positions: username.replace(' ','').split(',') for positions, username in body.items()
+ positions: username.replace(' ', '').split(',') for positions, username in body.items()
if username
}
diff --git a/ceod/transactions/members/UpdateMemberPositionsTransaction.py b/ceod/transactions/members/UpdateMemberPositionsTransaction.py
index 56fff2d..7193e3e 100644
--- a/ceod/transactions/members/UpdateMemberPositionsTransaction.py
+++ b/ceod/transactions/members/UpdateMemberPositionsTransaction.py
@@ -21,7 +21,7 @@ class UpdateMemberPositionsTransaction(AbstractTransaction):
'subscribe_to_mailing_lists',
]
- def __init__(self, positions_reversed: Dict[str, Union[str,list]]):
+ def __init__(self, positions_reversed: Dict[str, Union[str, list]]):
# positions_reversed is position -> username
super().__init__()
self.ldap_srv = component.getUtility(ILDAPService)
@@ -36,7 +36,7 @@ class UpdateMemberPositionsTransaction(AbstractTransaction):
self.positions[user].append(position)
else:
raise TypeError("Username(s) under each position must either be a string or a list")
-
+
# a cached Dict of the Users who need to be modified (username -> User)
self.users: Dict[str, IUser] = {}
--
2.39.5
From 910449d67a09d04995f7666f7c38d4a70698c504 Mon Sep 17 00:00:00 2001
From: Justin Chung <20733699+justin13888@users.noreply.github.com>
Date: Fri, 14 Oct 2022 13:21:12 -0400
Subject: [PATCH 3/5] Remove some logging code
---
ceo/tui/controllers/SetPositionsController.py | 6 ------
1 file changed, 6 deletions(-)
diff --git a/ceo/tui/controllers/SetPositionsController.py b/ceo/tui/controllers/SetPositionsController.py
index a3937ff..b2ce36b 100644
--- a/ceo/tui/controllers/SetPositionsController.py
+++ b/ceo/tui/controllers/SetPositionsController.py
@@ -8,11 +8,6 @@ import ceo.tui.utils as tui_utils
from ceo.tui.views import TransactionView
from ceod.transactions.members import UpdateMemberPositionsTransaction
-from ceo_common.logger_factory import logger_factory
-
-logger = logger_factory(__name__)
-
-
class SetPositionsController(Controller):
def __init__(self, model, app):
super().__init__(model, app)
@@ -22,7 +17,6 @@ class SetPositionsController(Controller):
for pos, field in self.view.position_fields.items():
if field.edit_text != '':
body[pos] = field.edit_text
- logger.info(body)
model = TransactionModel(
UpdateMemberPositionsTransaction.operations,
'POST', '/api/positions', json=body
--
2.39.5
From 41454913737073d66eafe98ec063fb64a86b2dad Mon Sep 17 00:00:00 2001
From: Justin Chung <20733699+justin13888@users.noreply.github.com>
Date: Fri, 14 Oct 2022 13:31:32 -0400
Subject: [PATCH 4/5] Fix linting error
---
ceo/tui/controllers/SetPositionsController.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/ceo/tui/controllers/SetPositionsController.py b/ceo/tui/controllers/SetPositionsController.py
index b2ce36b..17f8000 100644
--- a/ceo/tui/controllers/SetPositionsController.py
+++ b/ceo/tui/controllers/SetPositionsController.py
@@ -8,6 +8,7 @@ import ceo.tui.utils as tui_utils
from ceo.tui.views import TransactionView
from ceod.transactions.members import UpdateMemberPositionsTransaction
+
class SetPositionsController(Controller):
def __init__(self, model, app):
super().__init__(model, app)
--
2.39.5
From dbe1ef4d097ce04d50347f47c641b5fac6e64139 Mon Sep 17 00:00:00 2001
From: Max Erenberg
Date: Mon, 23 Jan 2023 02:15:13 -0500
Subject: [PATCH 5/5] use list to represent multiple users for a given position
---
ceo/cli/positions.py | 15 +++-
ceo/tui/controllers/GetPositionsController.py | 4 +-
ceo/tui/controllers/SetPositionsController.py | 6 +-
ceo/utils.py | 3 +
ceod/api/positions.py | 41 ++++++----
.../UpdateMemberPositionsTransaction.py | 25 +++---
docker-compose.yml | 2 +
docs/openapi.yaml | 57 +++++++------
docs/redoc-static.html | 27 ++++---
requirements.txt | 2 +-
tests/ceo/cli/test_db_mysql.py | 14 +++-
tests/ceo/cli/test_members.py | 4 +-
tests/ceo/cli/test_positions.py | 80 +++++++++++++++++--
tests/ceod/api/test_positions.py | 53 +++++++-----
14 files changed, 224 insertions(+), 109 deletions(-)
diff --git a/ceo/cli/positions.py b/ceo/cli/positions.py
index 733e8c0..b302a16 100644
--- a/ceo/cli/positions.py
+++ b/ceo/cli/positions.py
@@ -16,13 +16,22 @@ def positions():
def get():
resp = http_get('/api/positions')
result = handle_sync_response(resp)
- print_colon_kv(result.items())
+ print_colon_kv([
+ (position, ', '.join(usernames))
+ for position, usernames in result.items()
+ ])
@positions.command(short_help='Update positions')
def set(**kwargs):
- body = {k.replace('_', '-'): v for k, v in kwargs.items()}
- print_body = {k: v or '' for k, v in body.items()}
+ body = {
+ k.replace('_', '-'): v.replace(' ', '').split(',') if v else None
+ for k, v in kwargs.items()
+ }
+ print_body = {
+ k: ', '.join(v) if v else ''
+ for k, v in body.items()
+ }
click.echo('The positions will be updated:')
print_colon_kv(print_body.items())
click.confirm('Do you want to continue?', abort=True)
diff --git a/ceo/tui/controllers/GetPositionsController.py b/ceo/tui/controllers/GetPositionsController.py
index 3c40ec1..9c89410 100644
--- a/ceo/tui/controllers/GetPositionsController.py
+++ b/ceo/tui/controllers/GetPositionsController.py
@@ -19,8 +19,8 @@ class GetPositionsController(Controller):
positions = tui_utils.handle_sync_response(resp, self)
except Controller.RequestFailed:
return
- for pos, username in positions.items():
- self.model.positions[pos] = username
+ for pos, usernames in positions.items():
+ self.model.positions[pos] = ','.join(usernames)
def target():
self.view.flash_text.set_text('')
diff --git a/ceo/tui/controllers/SetPositionsController.py b/ceo/tui/controllers/SetPositionsController.py
index 17f8000..2669e7c 100644
--- a/ceo/tui/controllers/SetPositionsController.py
+++ b/ceo/tui/controllers/SetPositionsController.py
@@ -17,7 +17,7 @@ class SetPositionsController(Controller):
body = {}
for pos, field in self.view.position_fields.items():
if field.edit_text != '':
- body[pos] = field.edit_text
+ body[pos] = field.edit_text.replace(' ', '').split(',')
model = TransactionModel(
UpdateMemberPositionsTransaction.operations,
'POST', '/api/positions', json=body
@@ -37,8 +37,8 @@ class SetPositionsController(Controller):
positions = tui_utils.handle_sync_response(resp, self)
except Controller.RequestFailed:
return
- for pos, username in positions.items():
- self.model.positions[pos] = username
+ for pos, usernames in positions.items():
+ self.model.positions[pos] = ','.join(usernames)
def target():
self.view.flash_text.set_text('')
diff --git a/ceo/utils.py b/ceo/utils.py
index abc96bb..b053f7d 100644
--- a/ceo/utils.py
+++ b/ceo/utils.py
@@ -87,6 +87,9 @@ def space_colon_kv(pairs: List[Tuple[str, str]]) -> List[str]:
maxlen = max(len(key) for key, val in pairs)
for key, val in pairs:
if key != '':
+ if not val:
+ lines.append(key + ':')
+ continue
prefix = key + ': '
else:
# assume this is a continuation from the previous line
diff --git a/ceod/api/positions.py b/ceod/api/positions.py
index 7bf7243..14bb9c0 100644
--- a/ceod/api/positions.py
+++ b/ceod/api/positions.py
@@ -2,6 +2,7 @@ from flask import Blueprint, request
from zope import component
from .utils import authz_restrict_to_syscom, create_streaming_response
+from ceo_common.errors import BadRequest
from ceo_common.interfaces import ILDAPService, IConfig
from ceod.transactions.members import UpdateMemberPositionsTransaction
@@ -15,10 +16,9 @@ def get_positions():
positions = {}
for user in ldap_srv.get_users_with_positions():
for position in user.positions:
- if position in positions:
- positions[position] += f", {user.uid}"
- else:
- positions[position] = user.uid
+ if position not in positions:
+ positions[position] = []
+ positions[position].append(user.uid)
return positions
@@ -34,22 +34,29 @@ def update_positions():
# remove falsy values and parse multiple users in each position
# Example: "user1,user2, user3" -> ["user1","user2","user3"]
- body = {
- positions: username.replace(' ', '').split(',') for positions, username in body.items()
- if username
- }
+ position_to_usernames = {}
+ for position, usernames in body.items():
+ if not usernames:
+ continue
+ if type(usernames) is list:
+ position_to_usernames[position] = usernames
+ elif type(usernames) is str:
+ position_to_usernames[position] = usernames.replace(' ', '').split(',')
+ else:
+ raise BadRequest('usernames must be a list or comma-separated string')
- for position in body.keys():
+ # check for duplicates (i.e. one username specified twice in the same list)
+ for usernames in position_to_usernames.values():
+ if len(usernames) != len(set(usernames)):
+ raise BadRequest('username may only be specified at most once for a position')
+
+ for position in position_to_usernames.keys():
if position not in available:
- return {
- 'error': f'unknown position: {position}'
- }, 400
+ raise BadRequest(f'unknown position: {position}')
for position in required:
- if position not in body:
- return {
- 'error': f'missing required position: {position}'
- }, 400
+ if position not in position_to_usernames:
+ raise BadRequest(f'missing required position: {position}')
- txn = UpdateMemberPositionsTransaction(body)
+ txn = UpdateMemberPositionsTransaction(position_to_usernames)
return create_streaming_response(txn)
diff --git a/ceod/transactions/members/UpdateMemberPositionsTransaction.py b/ceod/transactions/members/UpdateMemberPositionsTransaction.py
index 7193e3e..8221f6a 100644
--- a/ceod/transactions/members/UpdateMemberPositionsTransaction.py
+++ b/ceod/transactions/members/UpdateMemberPositionsTransaction.py
@@ -1,8 +1,7 @@
from collections import defaultdict
-from typing import Dict
+from typing import Dict, List
from zope import component
-from typing import Union
from ..AbstractTransaction import AbstractTransaction
from ceo_common.interfaces import ILDAPService, IConfig, IUser
@@ -21,21 +20,19 @@ class UpdateMemberPositionsTransaction(AbstractTransaction):
'subscribe_to_mailing_lists',
]
- def __init__(self, positions_reversed: Dict[str, Union[str, list]]):
+ def __init__(self, position_to_usernames: Dict[str, List[str]]):
# positions_reversed is position -> username
super().__init__()
self.ldap_srv = component.getUtility(ILDAPService)
# Reverse the dict so it's easier to use (username -> positions)
self.positions = defaultdict(list)
- for position, username in positions_reversed.items():
- if isinstance(username, str):
- self.positions[username].append(position)
- elif isinstance(username, list):
- for user in username:
- self.positions[user].append(position)
+ for position, usernames in position_to_usernames.items():
+ if isinstance(usernames, list):
+ for username in usernames:
+ self.positions[username].append(position)
else:
- raise TypeError("Username(s) under each position must either be a string or a list")
+ raise TypeError("Username(s) under each position must be a list")
# a cached Dict of the Users who need to be modified (username -> User)
self.users: Dict[str, IUser] = {}
@@ -49,7 +46,7 @@ class UpdateMemberPositionsTransaction(AbstractTransaction):
mailing_lists = cfg.get('auxiliary mailing lists_exec')
# position -> username
- new_positions_reversed = {} # For returning result
+ new_position_to_usernames = {} # For returning result
# retrieve User objects and cache them
for username in self.positions:
@@ -71,7 +68,9 @@ class UpdateMemberPositionsTransaction(AbstractTransaction):
self.old_positions[username] = old_positions
for position in new_positions:
- new_positions_reversed[position] = username
+ if position not in new_position_to_usernames:
+ new_position_to_usernames[position] = []
+ new_position_to_usernames[position].append(username)
yield 'update_positions_ldap'
# update exec group in LDAP
@@ -104,7 +103,7 @@ class UpdateMemberPositionsTransaction(AbstractTransaction):
else:
yield 'subscribe_to_mailing_lists'
- self.finish(new_positions_reversed)
+ self.finish(new_position_to_usernames)
def rollback(self):
if 'update_exec_group_ldap' in self.finished_operations:
diff --git a/docker-compose.yml b/docker-compose.yml
index 59d246c..4db5345 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -4,6 +4,8 @@ x-common: &common
image: python:3.9-bullseye
volumes:
- .:$PWD:z
+ security_opt:
+ - label:disable
environment:
FLASK_APP: ceod.api
FLASK_ENV: development
diff --git a/docs/openapi.yaml b/docs/openapi.yaml
index 3bbe59d..c98a8e4 100644
--- a/docs/openapi.yaml
+++ b/docs/openapi.yaml
@@ -61,9 +61,9 @@ paths:
program:
$ref: "#/components/schemas/Program"
terms:
- $ref: "#/components/schemas/Terms"
+ $ref: "#/components/schemas/TermsOrNumTerms"
non_member_terms:
- $ref: "#/components/schemas/NonMemberTerms"
+ $ref: "#/components/schemas/TermsOrNumTerms"
forwarding_addresses:
$ref: "#/components/schemas/ForwardingAddresses"
responses:
@@ -161,17 +161,11 @@ paths:
- type: object
properties:
terms:
- type: array
- description: Terms for which this user will be a member
- items:
- $ref: "#/components/schemas/Term"
+ $ref: "#/components/schemas/TermsOrNumTerms"
- type: object
properties:
non_member_terms:
- type: array
- description: Terms for which this user will be a club rep
- items:
- $ref: "#/components/schemas/Term"
+ $ref: "#/components/schemas/TermsOrNumTerms"
example: {"terms": ["f2021"]}
responses:
"200":
@@ -383,11 +377,14 @@ paths:
schema:
type: object
additionalProperties:
- type: string
+ type: array
+ description: list of usernames
+ items:
+ type: string
example:
- president: user0
- vice-president: user1
- sysadmin: user2
+ president: ["user1"]
+ vice-president: ["user2", "user3"]
+ sysadmin: ["user4"]
treasurer:
post:
tags: ['positions']
@@ -404,11 +401,18 @@ paths:
schema:
type: object
additionalProperties:
- type: string
+ oneOf:
+ - type: string
+ description: username or comma-separated list of usernames
+ - type: array
+ description: list of usernames
+ items:
+ type: string
example:
- president: user0
- vice-president: user1
- sysadmin: user2
+ president: user1
+ vice-president: user2, user3
+ secretary: ["user4", "user5"]
+ sysadmin: ["user6"]
treasurer:
responses:
"200":
@@ -422,7 +426,7 @@ paths:
{"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"}
+ {"status": "completed", "result": {"president": ["user1"],"vice-president": ["user2", "user3"],"secretary": ["user4". "user5"],"sysadmin": ["user6"]}}
"400":
description: Failed
content:
@@ -883,14 +887,15 @@ components:
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
+ description: List of terms
items:
$ref: "#/components/schemas/Term"
+ TermsOrNumTerms:
+ oneOf:
+ - type: integer
+ description: number of additional terms to add
+ example: 1
+ - $ref: "#/components/schemas/Terms"
LoginShell:
type: string
description: Login shell
@@ -939,7 +944,7 @@ components:
terms:
$ref: "#/components/schemas/Terms"
non_member_terms:
- $ref: "#/components/schemas/NonMemberTerms"
+ $ref: "#/components/schemas/Terms"
forwarding_addresses:
$ref: "#/components/schemas/ForwardingAddresses"
groups:
diff --git a/docs/redoc-static.html b/docs/redoc-static.html
index 23f935e..0aeeea6 100644
--- a/docs/redoc-static.html
+++ b/docs/redoc-static.html
@@ -1854,13 +1854,14 @@ h1:hover > .sc-crzoAE::before,h2:hover > .iUxAWq::before,.iUxAWq:hover::before{v
data-styled.g14[id="sc-crzoAE"]{content:"iUxAWq,"}/*!sc*/
.dvcDrG{height:18px;width:18px;min-width:18px;vertical-align:middle;float:right;-webkit-transition:-webkit-transform 0.2s ease-out;-webkit-transition:transform 0.2s ease-out;transition:transform 0.2s ease-out;-webkit-transform:rotateZ(-90deg);-ms-transform:rotateZ(-90deg);transform:rotateZ(-90deg);}/*!sc*/
.iPqByX{height:1.3em;width:1.3em;min-width:1.3em;vertical-align:middle;-webkit-transition:-webkit-transform 0.2s ease-out;-webkit-transition:transform 0.2s ease-out;transition:transform 0.2s ease-out;-webkit-transform:rotateZ(-90deg);-ms-transform:rotateZ(-90deg);transform:rotateZ(-90deg);}/*!sc*/
+.hGHhhO{height:18px;width:18px;min-width:18px;vertical-align:middle;-webkit-transition:-webkit-transform 0.2s ease-out;-webkit-transition:transform 0.2s ease-out;transition:transform 0.2s ease-out;-webkit-transform:rotateZ(-90deg);-ms-transform:rotateZ(-90deg);transform:rotateZ(-90deg);}/*!sc*/
.dqYXmg{height:1.5em;width:1.5em;min-width:1.5em;vertical-align:middle;float:left;-webkit-transition:-webkit-transform 0.2s ease-out;-webkit-transition:transform 0.2s ease-out;transition:transform 0.2s ease-out;-webkit-transform:rotateZ(-90deg);-ms-transform:rotateZ(-90deg);transform:rotateZ(-90deg);}/*!sc*/
.dqYXmg polygon{fill:#1d8127;}/*!sc*/
.bRmrKA{height:20px;width:20px;min-width:20px;vertical-align:middle;float:right;-webkit-transition:-webkit-transform 0.2s ease-out;-webkit-transition:transform 0.2s ease-out;transition:transform 0.2s ease-out;-webkit-transform:rotateZ(0);-ms-transform:rotateZ(0);transform:rotateZ(0);}/*!sc*/
.bRmrKA polygon{fill:white;}/*!sc*/
.dVWHLw{height:1.5em;width:1.5em;min-width:1.5em;vertical-align:middle;float:left;-webkit-transition:-webkit-transform 0.2s ease-out;-webkit-transition:transform 0.2s ease-out;transition:transform 0.2s ease-out;-webkit-transform:rotateZ(-90deg);-ms-transform:rotateZ(-90deg);transform:rotateZ(-90deg);}/*!sc*/
.dVWHLw polygon{fill:#d41f1c;}/*!sc*/
-data-styled.g15[id="sc-dIsUp"]{content:"dvcDrG,iPqByX,dqYXmg,bRmrKA,dVWHLw,"}/*!sc*/
+data-styled.g15[id="sc-dIsUp"]{content:"dvcDrG,iPqByX,hGHhhO,dqYXmg,bRmrKA,dVWHLw,"}/*!sc*/
.iwcKgn{border-left:1px solid #7c7cbb;box-sizing:border-box;position:relative;padding:10px 10px 10px 0;}/*!sc*/
@media screen and (max-width:50rem){.iwcKgn{display:block;overflow:hidden;}}/*!sc*/
tr:first-of-type > .sc-hBMUJo,tr.last > .iwcKgn{border-left-width:0;background-position:top left;background-repeat:no-repeat;background-size:1px 100%;}/*!sc*/
@@ -2068,6 +2069,11 @@ data-styled.g53[id="sc-cOifOu"]{content:"hlhNtL,"}/*!sc*/
data-styled.g54[id="sc-Arkif"]{content:"jojbRz,"}/*!sc*/
.dSaTNC{margin-top:15px;}/*!sc*/
data-styled.g57[id="sc-jgPyTC"]{content:"dSaTNC,"}/*!sc*/
+.hTttpy button{background-color:transparent;border:0;outline:0;font-size:13px;font-family:Courier,monospace;cursor:pointer;padding:0;color:#333333;}/*!sc*/
+.hTttpy button:focus{font-weight:600;}/*!sc*/
+.hTttpy .sc-dIsUp{height:1.1em;width:1.1em;}/*!sc*/
+.hTttpy .sc-dIsUp polygon{fill:#666;}/*!sc*/
+data-styled.g58[id="sc-gSYDnn"]{content:"hTttpy,"}/*!sc*/
.jWaWWE{vertical-align:middle;font-size:13px;line-height:20px;}/*!sc*/
data-styled.g59[id="sc-laZMeE"]{content:"jWaWWE,"}/*!sc*/
.jrLlAa{color:rgba(102,102,102,0.9);}/*!sc*/
@@ -2259,11 +2265,9 @@ in real time.
sn given_name program terms Array of strings (Terms)
Terms for which this user was a member
-
non_member_terms Array of strings (NonMemberTerms)
Terms for which this user was a club rep
-
forwarding_addresses Array of strings <email> (ForwardingAddresses)
Forwarding addresses in ~/.forward
+
terms integer or Array of Terms (strings) (TermsOrNumTerms)
non_member_terms integer or Array of Terms (strings) (TermsOrNumTerms)
forwarding_addresses Array of strings <email> (ForwardingAddresses)
Forwarding addresses in ~/.forward
post /members
https://phosphoric-acid.csclub.uwaterloo.ca:9987/api /members
Request samples Content type application/json
Copy
Expand all Collapse all Response samples { "status" : "in progress" , "operation" : "add_user_to_ldap" }
+post /members
https://phosphoric-acid.csclub.uwaterloo.ca:9987/api /members
Request samples Content type application/json
Copy
Expand all Collapse all Response samples { "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" }
@@ -2286,8 +2290,7 @@ in real time.
{ "status" : "completed" , "result" : "OK" }
Renew a user Add member or non-member terms to a user
path Parameters username required
string
username of the user to renew
-
Request Body schema: application/json
One of object object
terms Array of strings (Term)
Terms for which this user will be a member
-
Request Body schema: application/json
One of object object
terms integer or Array of Terms (strings) (TermsOrNumTerms)
post /members/{username}/renew
https://phosphoric-acid.csclub.uwaterloo.ca:9987/api /members/{username}/renew
Request samples Content type application/json
Copy
Expand all Collapse all Response samples Content type application/json
Copy
Expand all Collapse all
Reset a user's password 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.
path Parameters username required
string
username of the user whose password will be reset
@@ -2402,17 +2405,17 @@ If the namespace already exists, the certificate inside the kubeconfig will be r
post /cloud/k8s/account/create
https://biloba.csclub.uwaterloo.ca:9987/api /cloud/k8s/account/create
Response samples Content type application/json
{ "status" : "OK" ,
"kubeconfig" : "string"
}
Show current positions Shows the list of positions and members holding them.
get /positions
https://phosphoric-acid.csclub.uwaterloo.ca:9987/api /positions
Response samples Content type application/json
Update positions 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.
+
get /positions
https://phosphoric-acid.csclub.uwaterloo.ca:9987/api /positions
Response samples Content type application/json
Copy
Expand all Collapse all Update positions 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.
Request Body schema: application/json property name* additional property
Responses property name* additional property
string or Array of strings
post /positions
https://phosphoric-acid.csclub.uwaterloo.ca:9987/api /positions
Request samples Content type application/json
Response samples { "status" : "in progress" , "operation" : "update_positions_ldap" }
+post /positions
https://phosphoric-acid.csclub.uwaterloo.ca:9987/api /positions
Request samples Content type application/json
Copy
Expand all Collapse all Response samples { "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" }
+{ "status" : "completed" , "result" : { "president" : [ "user1" ] , "vice-president" : [ "user2" , "user3" ] , "secretary" : [ "user4" . "user5" ] , "sysadmin" : [ "user6" ] } }