mirror of https://gitlab.com/bashrc2/epicyon
parent
57015d6000
commit
7e48beb0fe
|
@ -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
|
||||
|
||||
|
|
5
city.py
5
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()
|
||||
|
|
26
daemon.py
26
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)
|
||||
|
|
111
epicyon.py
111
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,
|
||||
|
|
|
@ -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)
|
||||
|
|
40
person.py
40
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',
|
||||
|
|
281
roles.py
281
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
|
||||
|
|
104
tests.py
104
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')
|
||||
|
|
|
@ -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 += \
|
||||
'<div class="roles">\n<h2>' + project + \
|
||||
'</h2>\n<div class="roles-inner">\n'
|
||||
for role in rolesList:
|
||||
if translate.get(role):
|
||||
profileStr += '<h3>' + translate[role] + '</h3>\n'
|
||||
else:
|
||||
profileStr += '<h3>' + role + '</h3>\n'
|
||||
profileStr += '</div></div>\n'
|
||||
profileStr += \
|
||||
'<div class="roles">\n<div class="roles-inner">\n'
|
||||
for role in rolesList:
|
||||
if translate.get(role):
|
||||
profileStr += '<h3>' + translate[role] + '</h3>\n'
|
||||
else:
|
||||
profileStr += '<h3>' + role + '</h3>\n'
|
||||
profileStr += '</div></div>\n'
|
||||
if len(profileStr) == 0:
|
||||
profileStr += \
|
||||
'<p>@' + nickname + '@' + domain + ' has no roles assigned</p>\n'
|
||||
|
|
Loading…
Reference in New Issue