From 7e48beb0fe813f04b8829964a0b31eb7c2b4b1be Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Thu, 13 May 2021 20:58:16 +0100 Subject: [PATCH] Remove role delegation The keeps the handling of roles very simple --- README_commandline.md | 46 ------- city.py | 5 +- daemon.py | 26 ++-- epicyon.py | 111 +---------------- outbox.py | 7 +- person.py | 40 ++++-- roles.py | 281 ++++++++---------------------------------- tests.py | 104 ++++------------ webapp_profile.py | 20 ++- 9 files changed, 140 insertions(+), 500 deletions(-) diff --git a/README_commandline.md b/README_commandline.md index af39f363d..9529671b9 100644 --- a/README_commandline.md +++ b/README_commandline.md @@ -310,52 +310,6 @@ python3 epicyon.py --domainmax 1000 --accountmax 200 With these settings you're going to be receiving no more than 200 messages for any given account within a day. -## Delegated roles - -Within an organization you may want to define different roles and for some projects to be delegated. By default the first account added to the system will be the admin, and be assigned *moderator* and *delegator* roles under a project called *instance*. The admin can then delegate a person to other projects with: - -``` bash -python3 epicyon.py --nickname [admin nickname] --domain [mydomain] \ - --delegate [person nickname] \ - --project [project name] --role [title] \ - --password [c2s password] -``` - -The other person could also be made a delegator, but they will only be able to delegate further within projects which they're assigned to. By design, this creates a restricted organizational hierarchy. For example: - -``` bash -python3 epicyon.py --nickname [admin nickname] --domain [mydomain] \ - --delegate [person nickname] \ - --project [project name] --role delegator \ - --password [c2s password] -``` - -A delegated role can also be removed. - -``` bash -python3 epicyon.py --nickname [admin nickname] --domain [mydomain] \ - --undelegate [person nickname] \ - --project [project name] \ - --password [c2s password] -``` - -This extends the ActivityPub client-to-server protocol to include activities called *Delegate* and *Role*. The JSON looks like: - -``` json -{ 'type': 'Delegate', - 'actor': https://somedomain/users/admin, - 'object': { - 'type': 'Role', - 'actor': https://'+somedomain+'/users/'+other, - 'object': 'otherproject;otherrole', - 'to': [], - 'cc': [] - }, - 'to': [], - 'cc': []} -``` - -Projects and roles are only scoped within a single instance. There presently are not enough security mechanisms to support multi-instance distributed organizations. ## Assigning skills diff --git a/city.py b/city.py index d34142908..3ea9983b7 100644 --- a/city.py +++ b/city.py @@ -128,7 +128,7 @@ def _getCityPulse(currTimeOfDay, decoySeed: int) -> (float, float): def spoofGeolocation(baseDir: str, city: str, currTime, decoySeed: int, - citiesList: []) -> (float, float, str, str, + citiesList: []) -> (float, float, str, str, \ str, str, int): """Given a city and the current time spoofs the location for an image @@ -150,7 +150,8 @@ def spoofGeolocation(baseDir: str, else: if not os.path.isfile(locationsFilename): return (default_latitude, default_longitude, - default_latdirection, default_longdirection) + default_latdirection, default_longdirection, + "", "", 0) cities = [] with open(locationsFilename, "r") as f: cities = f.readlines() diff --git a/daemon.py b/daemon.py index 82ada2b1f..023790a9c 100644 --- a/daemon.py +++ b/daemon.py @@ -124,6 +124,7 @@ from blocking import isBlockedHashtag from blocking import isBlockedDomain from blocking import getDomainBlocklist from roles import setRole +from roles import getRolesFromString from roles import clearModeratorStatus from roles import clearEditorStatus from roles import clearCounselorStatus @@ -4732,7 +4733,7 @@ class PubServer(BaseHTTPRequestHandler): if os.path.isdir(modDir): setRole(baseDir, modNick, domain, - 'instance', 'moderator') + 'moderator') else: # nicknames on separate lines modFile = open(moderatorsFile, "w+") @@ -4757,7 +4758,6 @@ class PubServer(BaseHTTPRequestHandler): if os.path.isdir(modDir): setRole(baseDir, modNick, domain, - 'instance', 'moderator') # change site editors list @@ -4789,7 +4789,7 @@ class PubServer(BaseHTTPRequestHandler): if os.path.isdir(edDir): setRole(baseDir, edNick, domain, - 'instance', 'editor') + 'editor') else: # nicknames on separate lines edFile = open(editorsFile, "w+") @@ -4814,7 +4814,6 @@ class PubServer(BaseHTTPRequestHandler): if os.path.isdir(edDir): setRole(baseDir, edNick, domain, - 'instance', 'editor') # change site counselors list @@ -4846,7 +4845,7 @@ class PubServer(BaseHTTPRequestHandler): if os.path.isdir(edDir): setRole(baseDir, edNick, domain, - 'instance', 'counselor') + 'counselor') else: # nicknames on separate lines edFile = open(counselorsFile, "w+") @@ -4871,7 +4870,6 @@ class PubServer(BaseHTTPRequestHandler): if os.path.isdir(edDir): setRole(baseDir, edNick, domain, - 'instance', 'counselor') # remove scheduled posts @@ -7376,7 +7374,7 @@ class PubServer(BaseHTTPRequestHandler): if not actorJson: return False - if actorJson.get('roles'): + if actorJson.get('affiliation'): if self._requestHTTP(): getPerson = \ personLookup(domain, path.replace('/roles', ''), @@ -7397,6 +7395,10 @@ class PubServer(BaseHTTPRequestHandler): if self.server.keyShortcuts.get(nickname): accessKeys = self.server.keyShortcuts[nickname] + rolesList = [] + if actorJson.get('affiliation'): + actorRolesStr = actorJson['affiliation']['roleName'] + rolesList = getRolesFromString(actorRolesStr) msg = \ htmlProfile(self.server.rssIconAtTop, self.server.cssCache, @@ -7420,8 +7422,7 @@ class PubServer(BaseHTTPRequestHandler): self.server.allowLocalNetworkAccess, self.server.textModeBanner, self.server.debug, - accessKeys, - actorJson['roles'], + accessKeys, rolesList, None, None) msg = msg.encode('utf-8') msglen = len(msg) @@ -7433,7 +7434,12 @@ class PubServer(BaseHTTPRequestHandler): 'show roles') else: if self._fetchAuthenticated(): - msg = json.dumps(actorJson['roles'], + rolesList = [] + if actorJson.get('affiliation'): + actorRolesStr = actorJson['affiliation']['roleName'] + rolesList = getRolesFromString(actorRolesStr) + + msg = json.dumps(rolesList, ensure_ascii=False) msg = msg.encode('utf-8') msglen = len(msg) diff --git a/epicyon.py b/epicyon.py index c7c6a4438..ab5997893 100644 --- a/epicyon.py +++ b/epicyon.py @@ -74,7 +74,6 @@ from media import getAttachmentMediaType from delete import sendDeleteViaServer from like import sendLikeViaServer from like import sendUndoLikeViaServer -from roles import sendRoleViaServer from skills import sendSkillViaServer from availability import setAvailability from availability import sendAvailabilityViaServer @@ -495,9 +494,6 @@ parser.add_argument('--maxEmoji', '--maxemoji', dest='maxEmoji', help='Maximum number of emoji within a post') parser.add_argument('--role', dest='role', type=str, default=None, help='Set a role for a person') -parser.add_argument('--organization', '--project', dest='project', - type=str, default=None, - help='Set a project for a person') parser.add_argument('--skill', dest='skill', type=str, default=None, help='Set a skill for a person') parser.add_argument('--level', dest='skillLevelPercent', type=int, @@ -518,11 +514,6 @@ parser.add_argument('--mute', dest='mute', type=str, default=None, help='Mute a particular post URL') parser.add_argument('--unmute', dest='unmute', type=str, default=None, help='Unmute a particular post URL') -parser.add_argument('--delegate', dest='delegate', type=str, default=None, - help='Address of an account to delegate a role to') -parser.add_argument('--undodelegate', '--undelegate', dest='undelegate', - type=str, default=None, - help='Removes a delegated role for the given address') parser.add_argument('--filter', dest='filterStr', type=str, default=None, help='Adds a word or phrase which if present will ' + 'cause a message to be ignored') @@ -1987,24 +1978,6 @@ if args.backgroundImage: print('Background image was not added for ' + args.nickname) sys.exit() -if args.project: - if not args.delegate and not args.undelegate: - if not nickname: - print('No nickname given') - sys.exit() - - if args.role.lower() == 'none' or \ - args.role.lower() == 'remove' or \ - args.role.lower() == 'delete': - args.role = None - if args.role: - if setRole(baseDir, nickname, domain, args.project, args.role): - print('Role within ' + args.project + ' set to ' + args.role) - else: - if setRole(baseDir, nickname, domain, args.project, None): - print('Left ' + args.project) - sys.exit() - if args.skill: if not nickname: print('Specify a nickname with the --nickname option') @@ -2218,86 +2191,6 @@ if args.unmute: time.sleep(1) sys.exit() -if args.delegate: - if not nickname: - print('Specify a nickname with the --nickname option') - sys.exit() - - if not args.password: - args.password = getpass.getpass('Password: ') - if not args.password: - print('Specify a password with the --password option') - sys.exit() - args.password = args.password.replace('\n', '') - - if not args.project: - print('Specify a project with the --project option') - sys.exit() - - if not args.role: - print('Specify a role with the --role option') - sys.exit() - - if '@' in args.delegate: - delegatedNickname = args.delegate.split('@')[0] - args.delegate = blockedActor - - session = createSession(proxyType) - personCache = {} - cachedWebfingers = {} - print('Sending delegation for ' + args.delegate + - ' with role ' + args.role + ' in project ' + args.project) - - sendRoleViaServer(baseDir, session, - nickname, args.password, - domain, port, - httpPrefix, args.delegate, - args.project, args.role, - cachedWebfingers, personCache, - True, __version__) - for i in range(10): - # TODO detect send success/fail - time.sleep(1) - sys.exit() - -if args.undelegate: - if not nickname: - print('Specify a nickname with the --nickname option') - sys.exit() - - if not args.password: - args.password = getpass.getpass('Password: ') - if not args.password: - print('Specify a password with the --password option') - sys.exit() - args.password = args.password.replace('\n', '') - - if not args.project: - print('Specify a project with the --project option') - sys.exit() - - if '@' in args.undelegate: - delegatedNickname = args.undelegate.split('@')[0] - args.undelegate = blockedActor - - session = createSession(proxyType) - personCache = {} - cachedWebfingers = {} - print('Sending delegation removal for ' + args.undelegate + - ' from role ' + args.role + ' in project ' + args.project) - - sendRoleViaServer(baseDir, session, - nickname, args.password, - domain, port, - httpPrefix, args.delegate, - args.project, None, - cachedWebfingers, personCache, - True, __version__) - for i in range(10): - # TODO detect send success/fail - time.sleep(1) - sys.exit() - if args.unblock: if not nickname: print('Specify a nickname with the --nickname option') @@ -2388,9 +2281,7 @@ if args.testdata: True, False, 'likewhateveryouwantscoob') setSkillLevel(baseDir, nickname, domain, 'testing', 60) setSkillLevel(baseDir, nickname, domain, 'typing', 50) - setRole(baseDir, nickname, domain, 'instance', 'admin') - setRole(baseDir, nickname, domain, 'epicyon', 'hacker') - setRole(baseDir, nickname, domain, 'someproject', 'assistant') + setRole(baseDir, nickname, domain, 'admin') setAvailability(baseDir, nickname, domain, 'busy') addShare(baseDir, diff --git a/outbox.py b/outbox.py index 8da8fa6d2..8f5562d62 100644 --- a/outbox.py +++ b/outbox.py @@ -35,7 +35,6 @@ from inbox import inboxUpdateIndex from announce import outboxAnnounce from announce import outboxUndoAnnounce from follow import outboxUndoFollow -from roles import outboxDelegate from skills import outboxSkills from availability import outboxAvailability from like import outboxLike @@ -313,7 +312,7 @@ def postMessageToOutbox(session, translate: {}, permittedOutboxTypes = ('Create', 'Announce', 'Like', 'Follow', 'Undo', 'Update', 'Add', 'Remove', 'Block', 'Delete', - 'Delegate', 'Skill', 'Add', 'Remove', 'Event', + 'Skill', 'Add', 'Remove', 'Event', 'Ignore') if messageJson['type'] not in permittedOutboxTypes: if debug: @@ -461,10 +460,6 @@ def postMessageToOutbox(session, translate: {}, print('DEBUG: handle any unfollow requests') outboxUndoFollow(baseDir, messageJson, debug) - if debug: - print('DEBUG: handle delegation requests') - outboxDelegate(baseDir, postToNickname, messageJson, debug) - if debug: print('DEBUG: handle skills changes requests') outboxSkills(baseDir, postToNickname, messageJson, debug) diff --git a/person.py b/person.py index c029a81ec..c6ca823e3 100644 --- a/person.py +++ b/person.py @@ -200,7 +200,9 @@ def getDefaultPersonContext() -> str: 'suspended': 'toot:suspended', 'toot': 'http://joinmastodon.org/ns#', 'value': 'schema:value', - 'Occupation': 'schema:Occupation' + 'Occupation': 'schema:Occupation', + 'OrganizationRole': 'schema:OrganizationRole', + 'WebSite': 'schema:Project' } @@ -281,7 +283,15 @@ def _createPersonBase(baseDir: str, nickname: str, domain: str, port: int, 'name': "", 'skills': "" }, - 'roles': {}, + "affiliation": { + "@type": "OrganizationRole", + "roleName": "", + "affiliation": { + "@type": "WebSite", + "url": httpPrefix + '://' + domain + }, + "startDate": published + }, 'availability': None, 'icon': { 'mediaType': 'image/png', @@ -319,7 +329,8 @@ def _createPersonBase(baseDir: str, nickname: str, domain: str, port: int, if newPerson.get('skills'): del newPerson['skills'] del newPerson['shares'] - del newPerson['roles'] + if newPerson.get('roles'): + del newPerson['roles'] del newPerson['tag'] del newPerson['availability'] del newPerson['followers'] @@ -463,10 +474,9 @@ def createPerson(baseDir: str, nickname: str, domain: str, port: int, if nickname != 'news': # print(nickname+' becomes the instance admin and a moderator') setConfigParam(baseDir, 'admin', nickname) - setRole(baseDir, nickname, domain, 'instance', 'admin') - setRole(baseDir, nickname, domain, 'instance', 'moderator') - setRole(baseDir, nickname, domain, 'instance', 'editor') - setRole(baseDir, nickname, domain, 'instance', 'delegator') + setRole(baseDir, nickname, domain, 'admin') + setRole(baseDir, nickname, domain, 'moderator') + setRole(baseDir, nickname, domain, 'editor') if not os.path.isdir(baseDir + '/accounts'): os.mkdir(baseDir + '/accounts') @@ -582,6 +592,22 @@ def personUpgradeActor(baseDir: str, personJson: {}, del personJson['skills'] updateActor = True + if not personJson.get('affiliation'): + personJson['affiliation'] = { + "@type": "OrganizationRole", + "roleName": "", + "affiliation": { + "@type": "WebSite", + "url": personJson['id'].split('/users/')[0] + }, + "startDate": published + } + updateActor = True + + if personJson.get('roles'): + del personJson['roles'] + updateActor = True + if updateActor: personJson['@context'] = [ 'https://www.w3.org/ns/activitystreams', diff --git a/roles.py b/roles.py index f198fe7f0..0ce857d49 100644 --- a/roles.py +++ b/roles.py @@ -37,10 +37,13 @@ def _clearRoleStatus(baseDir: str, role: str) -> None: actorJson = loadJson(filename) if not actorJson: continue - if actorJson['roles'].get('instance'): - if role in actorJson['roles']['instance']: - actorJson['roles']['instance'].remove(role) - saveJson(actorJson, filename) + if not actorJson.get('affiliation'): + continue + rolesList = \ + getRolesFromString(actorJson['affiliation']['roleName']) + if role in rolesList: + rolesList.remove(role) + saveJson(actorJson, filename) def clearEditorStatus(baseDir: str) -> None: @@ -112,13 +115,35 @@ def _removeRole(baseDir: str, nickname: str, roleFilename: str) -> None: f.write(roleNickname + '\n') +def setRolesFromList(actorJson: {}, rolesList: []) -> None: + """Sets roles from a list + """ + rolesStr = '' + for roleName in rolesList: + if rolesStr: + rolesStr += ', ' + rolesStr += roleName.lower() + if actorJson.get('affiliation'): + actorJson['affiliation']['roleName'] = rolesStr + + +def getRolesFromString(rolesStr: str) -> []: + """Returns a list of roles from a string + """ + rolesList = rolesStr.split(',') + rolesResult = [] + for roleName in rolesList: + rolesResult.append(roleName.strip().lower()) + return rolesResult + + def setRole(baseDir: str, nickname: str, domain: str, - project: str, role: str) -> bool: - """Set a person's role within a project + role: str) -> bool: + """Set a person's role Setting the role to an empty string or None will remove it """ # avoid giant strings - if len(role) > 128 or len(project) > 128: + if len(role) > 128: return False actorFilename = baseDir + '/accounts/' + \ nickname + '@' + domain + '.json' @@ -133,230 +158,28 @@ def setRole(baseDir: str, nickname: str, domain: str, actorJson = loadJson(actorFilename) if actorJson: + if not actorJson.get('affiliation'): + return False + rolesList = \ + getRolesFromString(actorJson['affiliation']['roleName']) + actorChanged = False if role: # add the role - if project == 'instance': - if roleFiles.get(role): - _addRole(baseDir, nickname, domain, roleFiles[role]) - if actorJson['roles'].get(project): - if role not in actorJson['roles'][project]: - actorJson['roles'][project].append(role) - else: - actorJson['roles'][project] = [role] + if roleFiles.get(role): + _addRole(baseDir, nickname, domain, roleFiles[role]) + if role not in rolesList: + rolesList.append(role) + rolesList.sort() + setRolesFromList(actorJson, rolesList) + actorChanged = True else: # remove the role - if project == 'instance': - if roleFiles.get(role): - _removeRole(baseDir, nickname, roleFiles[role]) - if actorJson['roles'].get(project): - actorJson['roles'][project].remove(role) - # if the project contains no roles then remove it - if len(actorJson['roles'][project]) == 0: - del actorJson['roles'][project] - saveJson(actorJson, actorFilename) + if roleFiles.get(role): + _removeRole(baseDir, nickname, roleFiles[role]) + if role in rolesList: + rolesList.remove(role) + setRolesFromList(actorJson, rolesList) + actorChanged = True + if actorChanged: + saveJson(actorJson, actorFilename) return True - - -def _getRoles(baseDir: str, nickname: str, domain: str, - project: str) -> []: - """Returns the roles for a given person on a given project - """ - actorFilename = baseDir + '/accounts/' + \ - nickname + '@' + domain + '.json' - if not os.path.isfile(actorFilename): - return False - - actorJson = loadJson(actorFilename) - if actorJson: - if not actorJson.get('roles'): - return None - if not actorJson['roles'].get(project): - return None - return actorJson['roles'][project] - return None - - -def outboxDelegate(baseDir: str, authenticatedNickname: str, - messageJson: {}, debug: bool) -> bool: - """Handles receiving a delegation request - """ - if not messageJson.get('type'): - return False - if not messageJson['type'] == 'Delegate': - return False - if not messageJson.get('object'): - return False - if not isinstance(messageJson['object'], dict): - return False - if not messageJson['object'].get('type'): - return False - if not messageJson['object']['type'] == 'Role': - return False - if not messageJson['object'].get('object'): - return False - if not messageJson['object'].get('actor'): - return False - if not isinstance(messageJson['object']['object'], str): - return False - if ';' not in messageJson['object']['object']: - print('WARN: No ; separator between project and role') - return False - - delegatorNickname = getNicknameFromActor(messageJson['actor']) - if delegatorNickname != authenticatedNickname: - return - domain, port = getDomainFromActor(messageJson['actor']) - project = messageJson['object']['object'].split(';')[0].strip() - - # instance delegators can delagate to other projects - # than their own - canDelegate = False - delegatorRoles = _getRoles(baseDir, delegatorNickname, - domain, 'instance') - if delegatorRoles: - if 'delegator' in delegatorRoles: - canDelegate = True - - if not canDelegate: - canDelegate = True - # non-instance delegators can only delegate within their project - delegatorRoles = _getRoles(baseDir, delegatorNickname, - domain, project) - if delegatorRoles: - if 'delegator' not in delegatorRoles: - return False - else: - return False - - if not canDelegate: - return False - nickname = getNicknameFromActor(messageJson['object']['actor']) - if not nickname: - print('WARN: unable to find nickname in ' + - messageJson['object']['actor']) - return False - role = \ - messageJson['object']['object'].split(';')[1].strip().lower() - - if not role: - setRole(baseDir, nickname, domain, project, None) - return True - - # what roles is this person already assigned to? - existingRoles = _getRoles(baseDir, nickname, domain, project) - if existingRoles: - if role in existingRoles: - if debug: - print(nickname + '@' + domain + - ' is already assigned to the role ' + - role + ' within the project ' + project) - return False - setRole(baseDir, nickname, domain, project, role) - if debug: - print(nickname + '@' + domain + - ' assigned to the role ' + role + - ' within the project ' + project) - return True - - -def sendRoleViaServer(baseDir: str, session, - delegatorNickname: str, password: str, - delegatorDomain: str, delegatorPort: int, - httpPrefix: str, nickname: str, - project: str, role: str, - cachedWebfingers: {}, personCache: {}, - debug: bool, projectVersion: str) -> {}: - """A delegator creates a role for a person via c2s - Setting role to an empty string or None removes the role - """ - if not session: - print('WARN: No session for sendRoleViaServer') - return 6 - - delegatorDomainFull = getFullDomain(delegatorDomain, delegatorPort) - - toUrl = \ - httpPrefix + '://' + delegatorDomainFull + '/users/' + nickname - ccUrl = \ - httpPrefix + '://' + delegatorDomainFull + '/users/' + \ - delegatorNickname + '/followers' - - if role: - roleStr = project.lower() + ';' + role.lower() - else: - roleStr = project.lower() + ';' - actor = \ - httpPrefix + '://' + delegatorDomainFull + \ - '/users/' + delegatorNickname - delegateActor = \ - httpPrefix + '://' + delegatorDomainFull + '/users/' + nickname - newRoleJson = { - 'type': 'Delegate', - 'actor': actor, - 'object': { - 'type': 'Role', - 'actor': delegateActor, - 'object': roleStr, - 'to': [toUrl], - 'cc': [ccUrl] - }, - 'to': [toUrl], - 'cc': [ccUrl] - } - - handle = \ - httpPrefix + '://' + delegatorDomainFull + '/@' + delegatorNickname - - # lookup the inbox for the To handle - wfRequest = webfingerHandle(session, handle, httpPrefix, - cachedWebfingers, - delegatorDomain, projectVersion, debug) - if not wfRequest: - if debug: - print('DEBUG: role webfinger failed for ' + handle) - return 1 - if not isinstance(wfRequest, dict): - print('WARN: role webfinger for ' + handle + - ' did not return a dict. ' + str(wfRequest)) - return 1 - - postToBox = 'outbox' - - # get the actor inbox for the To handle - (inboxUrl, pubKeyId, pubKey, - fromPersonId, sharedInbox, - avatarUrl, displayName) = getPersonBox(baseDir, session, - wfRequest, personCache, - projectVersion, httpPrefix, - delegatorNickname, - delegatorDomain, postToBox, - 765672) - - if not inboxUrl: - if debug: - print('DEBUG: role no ' + postToBox + - ' was found for ' + handle) - return 3 - if not fromPersonId: - if debug: - print('DEBUG: role no actor was found for ' + handle) - return 4 - - authHeader = createBasicAuthHeader(delegatorNickname, password) - - headers = { - 'host': delegatorDomain, - 'Content-type': 'application/json', - 'Authorization': authHeader - } - postResult = \ - postJson(session, newRoleJson, [], inboxUrl, headers, 30, True) - if not postResult: - if debug: - print('DEBUG: POST role failed for c2s to ' + inboxUrl) -# return 5 - - if debug: - print('DEBUG: c2s POST role success') - - return newRoleJson diff --git a/tests.py b/tests.py index e4b2300d0..1b53f0f2e 100644 --- a/tests.py +++ b/tests.py @@ -69,8 +69,9 @@ from person import setBio from skills import setSkillLevel from skills import setSkillsFromDict from skills import getSkillsFromString +from roles import setRolesFromList +from roles import getRolesFromString from roles import setRole -from roles import outboxDelegate from auth import constantTimeStringCheck from auth import createBasicAuthHeader from auth import authorizeBasic @@ -454,7 +455,7 @@ def createServerAlice(path: str, domain: str, port: int, deleteAllPosts(path, nickname, domain, 'inbox') deleteAllPosts(path, nickname, domain, 'outbox') assert setSkillLevel(path, nickname, domain, 'hacking', 90) - assert setRole(path, nickname, domain, 'someproject', 'guru') + assert setRole(path, nickname, domain, 'guru') if hasFollows: followPerson(path, nickname, domain, 'bob', bobAddress, federationList, False) @@ -558,8 +559,6 @@ def createServerBob(path: str, domain: str, port: int, False, password) deleteAllPosts(path, nickname, domain, 'inbox') deleteAllPosts(path, nickname, domain, 'outbox') - assert setRole(path, nickname, domain, 'bandname', 'bass player') - assert setRole(path, nickname, domain, 'bandname', 'publicist') if hasFollows: followPerson(path, nickname, domain, 'alice', aliceAddress, federationList, False) @@ -1411,80 +1410,6 @@ def testCreatePerson(): shutil.rmtree(baseDir) -def testDelegateRoles(): - print('testDelegateRoles') - currDir = os.getcwd() - nickname = 'test382' - nicknameDelegated = 'test383' - domain = 'badgerdomain.com' - password = 'mypass' - port = 80 - httpPrefix = 'https' - baseDir = currDir + '/.tests_delegaterole' - if os.path.isdir(baseDir): - shutil.rmtree(baseDir) - os.mkdir(baseDir) - os.chdir(baseDir) - - privateKeyPem, publicKeyPem, person, wfEndpoint = \ - createPerson(baseDir, nickname, domain, port, - httpPrefix, True, False, password) - privateKeyPem, publicKeyPem, person, wfEndpoint = \ - createPerson(baseDir, nicknameDelegated, domain, port, - httpPrefix, True, False, 'insecure') - - httpPrefix = 'http' - project = 'artechoke' - role = 'delegator' - actorDelegated = \ - httpPrefix + '://' + domain + '/users/' + nicknameDelegated - newRoleJson = { - 'type': 'Delegate', - 'actor': httpPrefix + '://' + domain + '/users/' + nickname, - 'object': { - 'type': 'Role', - 'actor': actorDelegated, - 'object': project + ';' + role, - 'to': [], - 'cc': [] - }, - 'to': [], - 'cc': [] - } - - assert outboxDelegate(baseDir, nickname, newRoleJson, False) - # second time delegation has already happened so should return false - assert outboxDelegate(baseDir, nickname, newRoleJson, False) is False - - assert '"delegator"' in open(baseDir + '/accounts/' + nickname + - '@' + domain + '.json').read() - assert '"delegator"' in open(baseDir + '/accounts/' + nicknameDelegated + - '@' + domain + '.json').read() - - newRoleJson = { - 'type': 'Delegate', - 'actor': httpPrefix + '://' + domain + '/users/' + nicknameDelegated, - 'object': { - 'type': 'Role', - 'actor': httpPrefix + '://' + domain + '/users/' + nickname, - 'object': 'otherproject;otherrole', - 'to': [], - 'cc': [] - }, - 'to': [], - 'cc': [] - } - - # non-delegators cannot assign roles - assert outboxDelegate(baseDir, nicknameDelegated, - newRoleJson, False) is False - assert '"otherrole"' not in open(baseDir + '/accounts/' + - nickname + '@' + domain + '.json').read() - - os.chdir(currDir) - shutil.rmtree(baseDir) - - def testAuthentication(): print('testAuthentication') currDir = os.getcwd() @@ -3755,9 +3680,31 @@ def testSkills() -> None: assert skillsDict['gardening'] == 70 +def testRoles() -> None: + print('testRoles') + actorJson = { + 'affiliation': { + "@type": "OrganizationRole", + "roleName": "", + "affiliation": { + "@type": "WebSite", + "url": "https://testinstance.org" + }, + "startDate": "date goes here" + } + } + testRolesList = ["admin", "moderator"] + setRolesFromList(actorJson, testRolesList) + assert actorJson['affiliation']['roleName'] + rolesList = getRolesFromString(actorJson['affiliation']['roleName']) + assert 'admin' in rolesList + assert 'moderator' in rolesList + + def runAllTests(): print('Running tests...') testFunctions() + testRoles() testSkills() testSpoofGeolocation() testRemovePostInteractions() @@ -3812,5 +3759,4 @@ def runAllTests(): testNoOfFollowersOnDomain() testFollows() testGroupFollowers() - testDelegateRoles() print('Tests succeeded\n') diff --git a/webapp_profile.py b/webapp_profile.py index ae42753a0..8b91ba5d6 100644 --- a/webapp_profile.py +++ b/webapp_profile.py @@ -1027,20 +1027,18 @@ def _htmlProfileFollowing(translate: {}, baseDir: str, httpPrefix: str, def _htmlProfileRoles(translate: {}, nickname: str, domain: str, - rolesJson: {}) -> str: + rolesList: []) -> str: """Shows roles on the profile screen """ profileStr = '' - for project, rolesList in rolesJson.items(): - profileStr += \ - '
\n

' + project + \ - '

\n
\n' - for role in rolesList: - if translate.get(role): - profileStr += '

' + translate[role] + '

\n' - else: - profileStr += '

' + role + '

\n' - profileStr += '
\n' + profileStr += \ + '
\n
\n' + for role in rolesList: + if translate.get(role): + profileStr += '

' + translate[role] + '

\n' + else: + profileStr += '

' + role + '

\n' + profileStr += '
\n' if len(profileStr) == 0: profileStr += \ '

@' + nickname + '@' + domain + ' has no roles assigned

\n'