def sync_member_roles(cursor: Cursor, member: Member) -> ResultSet: """ Adjust grants for society roles to match the given member's memberships. """ user = _user_name(member) current = set() seen = set() for database in mysql.get_user_databases(cursor, user): name = _database_name_rev(database) # Filter active roles to those owned by society accounts. if name == member.crsid: continue if name not in seen: try: get_society(name) except KeyError: continue else: seen.add(name) if name in seen: current.add((user, database)) needed = set() for role in mysql.get_users(cursor, *(_user_name(soc) for soc in member.societies)): databases = (_database_name(role), _database_name(role, "%")) needed.update({(user, database) for database in databases}) return _sync_roles(cursor, current, needed)
def resolve_references(self, sess): super(SocietyJob, self).resolve_references(sess) try: self.society = queries.get_society(self.society_society) except KeyError: # maybe the society doesn't exist yet / any more self.society = None
def create_society(sess: SQLASession, name: str, description: str, admins: Set[str], role_email: str = None) -> Result[Society]: """ Register or update a society in the database. """ try: soc = get_society(name, sess) except KeyError: soc = Society(society=name, description=description, admins=get_members(sess, *admins), role_email=role_email) sess.add(soc) state = State.success else: if admins != soc.admin_crsids: raise ValueError("Admins for {!r} are {}, expecting {}".format( name, soc.admin_crsids, admins)) soc.description = description soc.role_email = role_email state = State.success if sess.is_modified(soc) else State.unchanged return Result(state, soc)
def sync_member_roles(cursor: Cursor, member: Member) -> ResultSet: """ Adjust grants for society roles to match the given member's memberships. """ username = owner_name(member) current = set() for role in pgsql.get_user_roles(cursor, username): # Filter active roles to those owned by society accounts. if role[0] == member.crsid: continue try: get_society(role[0]) except KeyError: continue else: current.add((username, role)) roles = pgsql.get_roles(cursor, *(soc.society for soc in member.societies)) needed = set((username, role) for role in roles) return _sync_roles(cursor, current, needed)
def test_create_society(self): name = "test" description = "Test Society {}".format(self.now) role_email = "sysadmins-python-unittest-{}@srcf.net".format(self.now) soc = Society(society=name, description=description, role_email=role_email) sess.add(soc) sess.flush() got = queries.get_society(name, sess) self.assertIs(soc, got) self.assertIsInstance(soc.uid, int) self.assertIsInstance(soc.gid, int)
def sync_member_roles(cursor: Cursor, member: Member) -> Collect[None]: """ Adjust grants for society roles to match the given member's memberships. """ if not member.societies: return username = owner_name(member) current: Set[Tuple[str, pgsql.Role]] = set() for role in pgsql.get_user_roles(cursor, username): # Filter active roles to those owned by society accounts. if role[0] == member.crsid: continue try: get_society(role[0]) except KeyError: continue else: current.add((username, role)) roles = pgsql.get_roles(cursor, *(soc.society for soc in member.societies)) needed = set((username, role) for role in roles) yield from _sync_roles(cursor, current, needed)
def destroy_test_session(sess: Session) -> None: """ Remove the member and society records auto-generated in `create_test_session`, and revert the global session of `srcf.database.queries` to the default state. """ soc = queries.get_society("unittest", sess) mem = queries.get_member("spqr2", sess) sess.begin() soc.admins.remove(mem) sess.delete(soc) sess.delete(mem) sess.commit() queries._global_session = None queries._auto_create_global_session = True
def _sync_society_admins(sess: SQLASession, society: Society, admins: Set[str]) -> Collect[None]: society = get_society(society.society, sess) if society.admin_crsids == admins: return group = unix.get_group(society.gid) for crsid in admins - society.admin_crsids: member = get_member(crsid, sess) yield bespoke.add_society_admin(sess, member, society, group) for crsid in society.admin_crsids - admins: member = get_member(crsid, sess) yield bespoke.remove_society_admin(sess, member, society, group) with mysql.context() as cursor: yield mysql.sync_society_roles(cursor, society) with pgsql.context() as cursor: yield pgsql.sync_society_roles(cursor, society)
def wrap(opts: Optional[DocOptArgs] = None): extra: Dict[str, Any] = {} if opts is None: doc = cleandoc(fn.__doc__.format(script=label)) opts = docopt(doc) # Detect resolvable-typed arguments and fill in their values. sig = signature(fn) ok = True for param in sig.parameters.values(): name = param.name cls = param.annotation if cls is DocOptArgs: extra[name] = opts continue elif cls is SQLASession: extra[name] = sess continue try: try: value = cast(str, opts[name.upper()]) except KeyError: value = cast(str, opts["<{}>".format(name)]) except KeyError: raise RuntimeError("Missing argument {!r}".format(name)) try: if cls is Member: extra[name] = get_member(value, sess) elif cls is Society: extra[name] = get_society(value, sess) elif cls is Owner: extra[name] = get_member_or_society(value, sess) else: raise RuntimeError("Bad parameter {!r} type {!r}".format( name, cls)) except KeyError: ok = False error("{!r} is not valid for parameter {!r}".format( value, name), colour="1") if not ok: sys.exit(1) try: fn(**extra) except Exception: sess.rollback() else: sess.commit()
def remove_society_admin(member: Member, society: Society) -> ResultSet: """ Demote a member from a society account's list of admins. """ with bespoke.context() as sess: member = get_member(member.crsid, sess) society = get_society(society.society, sess) results = ResultSet(bespoke.remove_from_society(sess, member, society)) results.extend( unix.remove_from_group(unix.get_user(member.crsid), unix.get_group(society.society)), bespoke.link_soc_home_dir(member, society)) with mysql_context() as cursor: results.extend(mysql.sync_society_roles(cursor, member)) with pgsql_p.context() as cursor: results.extend(pgsql.sync_society_roles(cursor, member)) return results
def add_society_admin(member: Member, society: Society) -> ResultSet: """ Promote a member to a society account admin. """ with bespoke.context() as sess: # Re-fetch under current session for transaction safety. member = get_member(member.crsid, sess) society = get_society(society.society, sess) results = ResultSet(bespoke.add_to_society(sess, member, society)) results.extend( unix.add_to_group(unix.get_user(member.crsid), unix.get_group(society.society)), bespoke.link_soc_home_dir(member, society)) with mysql_context() as cursor: results.extend(mysql.sync_society_roles(cursor, member)) with pgsql_p.context() as cursor: results.extend(pgsql.sync_society_roles(cursor, member)) return results
def ensure_society(sess: SQLASession, name: str, description: str, role_email: Optional[str] = None) -> Collect[Society]: """ Register or update a society in the database. For existing societies, this will synchronise member relations with the given list of admins. """ try: society = get_society(name, sess) except KeyError: res_record = yield from _create_society(sess, name, description, role_email) society = res_record.value else: yield _update_society(sess, society, description, role_email) return society
def create_sysadmin(member: Member) -> ResultSet: """ Create an administrative account for an existing member. """ if not member.user: raise ValueError("{!r} is not an active user") username = "******".format(member.crsid) real_name = "{} (Sysadmin Account)".format(member.name) results = ResultSet() user = results.add(unix.create_user(username, real_name=real_name)).value results.extend(unix.add_to_group(user, unix.get_group("sysadmins")), unix.add_to_group(user, unix.get_group("adm"))) # TODO: sed -i~ -re "/^sysadmin/ s/$/ (,$admuser,)/" /etc/netgroup for soc in ("executive", "srcf-admin", "srcf-web"): results.extend(add_society_admin(member, get_society(soc))) with pgsql_p.context() as cursor: results.extend( pgsql_p.create_user(cursor, username), pgsql_p.grant_role(cursor, username, pgsql_p.get_role(cursor, "sysadmins"))) return results
def create_sysadmin(sess: SQLASession, member: Member, new_passwd: bool = False) -> Collect[Optional[Password]]: """ Create an administrative account for an existing member. """ if not member.user: raise ValueError("{!r} is not an active user") username = "******".format(member.crsid) real_name = "{} (Sysadmin Account)".format(member.name) res_group = yield from unix.ensure_group(username) group = res_group.value res_user = yield from unix.ensure_user(username, gid=group.gr_gid, real_name=real_name) new_user = res_user.state == State.created user = res_user.value if new_user or new_passwd: res_passwd = yield from unix.reset_password(user) passwd = res_passwd.value else: passwd = None if res_user or passwd: yield bespoke.update_nis(new_user) yield unix.create_home(user, os.path.join("/home", username)) yield unix.create_home(user, os.path.join("/public/home", username), True) yield bespoke.populate_home_dir(member) yield unix.add_to_group(user, unix.get_group("sysadmins")) yield unix.add_to_group(user, unix.get_group("adm")) yield unix.grant_netgroup(user, "sysadmins") for soc in ("executive", "srcf-admin", "srcf-web"): yield add_society_admin(sess, member, get_society(soc, sess)) with pgsql.context() as cursor: yield pgsql_p.ensure_user(cursor, username) yield pgsql_p.grant_role(cursor, username, pgsql_p.get_role(cursor, "sysadmins")) return passwd
def setUpClass(cls) -> None: cls.mem = queries.get_member("spqr2", sess) cls.soc = queries.get_society("unittest", sess)