mirror of https://gitlab.com/bashrc2/epicyon
Merge branch 'main' of ssh://code.freedombone.net:2222/bashrc/epicyon
commit
a6419a5647
|
|
@ -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.
|
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
|
## Assigning skills
|
||||||
|
|
||||||
|
|
|
||||||
3
city.py
3
city.py
|
|
@ -150,7 +150,8 @@ def spoofGeolocation(baseDir: str,
|
||||||
else:
|
else:
|
||||||
if not os.path.isfile(locationsFilename):
|
if not os.path.isfile(locationsFilename):
|
||||||
return (default_latitude, default_longitude,
|
return (default_latitude, default_longitude,
|
||||||
default_latdirection, default_longdirection)
|
default_latdirection, default_longdirection,
|
||||||
|
"", "", 0)
|
||||||
cities = []
|
cities = []
|
||||||
with open(locationsFilename, "r") as f:
|
with open(locationsFilename, "r") as f:
|
||||||
cities = f.readlines()
|
cities = f.readlines()
|
||||||
|
|
|
||||||
103
daemon.py
103
daemon.py
|
|
@ -98,6 +98,11 @@ from follow import getFollowingFeed
|
||||||
from follow import sendFollowRequest
|
from follow import sendFollowRequest
|
||||||
from follow import unfollowAccount
|
from follow import unfollowAccount
|
||||||
from follow import createInitialLastSeen
|
from follow import createInitialLastSeen
|
||||||
|
from skills import getSkillsFromString
|
||||||
|
from skills import noOfActorSkills
|
||||||
|
from skills import actorHasSkill
|
||||||
|
from skills import actorSkillValue
|
||||||
|
from skills import setActorSkillLevel
|
||||||
from auth import authorize
|
from auth import authorize
|
||||||
from auth import createPassword
|
from auth import createPassword
|
||||||
from auth import createBasicAuthHeader
|
from auth import createBasicAuthHeader
|
||||||
|
|
@ -119,6 +124,7 @@ from blocking import isBlockedHashtag
|
||||||
from blocking import isBlockedDomain
|
from blocking import isBlockedDomain
|
||||||
from blocking import getDomainBlocklist
|
from blocking import getDomainBlocklist
|
||||||
from roles import setRole
|
from roles import setRole
|
||||||
|
from roles import getRolesFromString
|
||||||
from roles import clearModeratorStatus
|
from roles import clearModeratorStatus
|
||||||
from roles import clearEditorStatus
|
from roles import clearEditorStatus
|
||||||
from roles import clearCounselorStatus
|
from roles import clearCounselorStatus
|
||||||
|
|
@ -4154,22 +4160,6 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
# which isn't implemented in Epicyon
|
# which isn't implemented in Epicyon
|
||||||
actorJson['discoverable'] = True
|
actorJson['discoverable'] = True
|
||||||
actorChanged = True
|
actorChanged = True
|
||||||
if not actorJson['@context'][2].get('orgSchema'):
|
|
||||||
actorJson['@context'][2]['orgSchema'] = \
|
|
||||||
'toot:orgSchema'
|
|
||||||
actorChanged = True
|
|
||||||
if not actorJson['@context'][2].get('skills'):
|
|
||||||
actorJson['@context'][2]['skills'] = 'toot:skills'
|
|
||||||
actorChanged = True
|
|
||||||
if not actorJson['@context'][2].get('shares'):
|
|
||||||
actorJson['@context'][2]['shares'] = 'toot:shares'
|
|
||||||
actorChanged = True
|
|
||||||
if not actorJson['@context'][2].get('roles'):
|
|
||||||
actorJson['@context'][2]['roles'] = 'toot:roles'
|
|
||||||
actorChanged = True
|
|
||||||
if not actorJson['@context'][2].get('availability'):
|
|
||||||
actorJson['@context'][2]['availaibility'] = \
|
|
||||||
'toot:availability'
|
|
||||||
if actorJson.get('capabilityAcquisitionEndpoint'):
|
if actorJson.get('capabilityAcquisitionEndpoint'):
|
||||||
del actorJson['capabilityAcquisitionEndpoint']
|
del actorJson['capabilityAcquisitionEndpoint']
|
||||||
actorChanged = True
|
actorChanged = True
|
||||||
|
|
@ -4207,7 +4197,7 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
|
|
||||||
# set skill levels
|
# set skill levels
|
||||||
skillCtr = 1
|
skillCtr = 1
|
||||||
newSkills = {}
|
actorSkillsCtr = noOfActorSkills(actorJson)
|
||||||
while skillCtr < 10:
|
while skillCtr < 10:
|
||||||
skillName = \
|
skillName = \
|
||||||
fields.get('skillName' + str(skillCtr))
|
fields.get('skillName' + str(skillCtr))
|
||||||
|
|
@ -4222,21 +4212,21 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
if not skillValue:
|
if not skillValue:
|
||||||
skillCtr += 1
|
skillCtr += 1
|
||||||
continue
|
continue
|
||||||
if not actorJson['skills'].get(skillName):
|
if not actorHasSkill(actorJson, skillName):
|
||||||
actorChanged = True
|
actorChanged = True
|
||||||
else:
|
else:
|
||||||
if actorJson['skills'][skillName] != \
|
if actorSkillValue(actorJson, skillName) != \
|
||||||
int(skillValue):
|
int(skillValue):
|
||||||
actorChanged = True
|
actorChanged = True
|
||||||
newSkills[skillName] = int(skillValue)
|
setActorSkillLevel(actorJson,
|
||||||
|
skillName, int(skillValue))
|
||||||
skillsStr = self.server.translate['Skills']
|
skillsStr = self.server.translate['Skills']
|
||||||
setHashtagCategory(baseDir, skillName,
|
setHashtagCategory(baseDir, skillName,
|
||||||
skillsStr.lower())
|
skillsStr.lower())
|
||||||
skillCtr += 1
|
skillCtr += 1
|
||||||
if len(actorJson['skills'].items()) != \
|
if noOfActorSkills(actorJson) != \
|
||||||
len(newSkills.items()):
|
actorSkillsCtr:
|
||||||
actorChanged = True
|
actorChanged = True
|
||||||
actorJson['skills'] = newSkills
|
|
||||||
|
|
||||||
# change password
|
# change password
|
||||||
if fields.get('password'):
|
if fields.get('password'):
|
||||||
|
|
@ -4593,19 +4583,20 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
|
|
||||||
# Other accounts (alsoKnownAs)
|
# Other accounts (alsoKnownAs)
|
||||||
occupationName = ""
|
occupationName = ""
|
||||||
if actorJson.get('occupationName'):
|
if actorJson.get('hasOccupation'):
|
||||||
occupationName = actorJson['occupationName']
|
if actorJson['hasOccupation'].get('name'):
|
||||||
|
occupationName = actorJson['hasOccupation']['name']
|
||||||
if fields.get('occupationName'):
|
if fields.get('occupationName'):
|
||||||
fields['occupationName'] = \
|
fields['occupationName'] = \
|
||||||
removeHtml(fields['occupationName'])
|
removeHtml(fields['occupationName'])
|
||||||
if occupationName != \
|
if occupationName != \
|
||||||
fields['occupationName']:
|
fields['occupationName']:
|
||||||
actorJson['occupationName'] = \
|
actorJson['hasOccupation']['name'] = \
|
||||||
fields['occupationName']
|
fields['occupationName']
|
||||||
actorChanged = True
|
actorChanged = True
|
||||||
else:
|
else:
|
||||||
if occupationName:
|
if occupationName:
|
||||||
actorJson['occupationName'] = ''
|
actorJson['hasOccupation']['name'] = ''
|
||||||
actorChanged = True
|
actorChanged = True
|
||||||
|
|
||||||
# Other accounts (alsoKnownAs)
|
# Other accounts (alsoKnownAs)
|
||||||
|
|
@ -4742,7 +4733,7 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
if os.path.isdir(modDir):
|
if os.path.isdir(modDir):
|
||||||
setRole(baseDir,
|
setRole(baseDir,
|
||||||
modNick, domain,
|
modNick, domain,
|
||||||
'instance', 'moderator')
|
'moderator')
|
||||||
else:
|
else:
|
||||||
# nicknames on separate lines
|
# nicknames on separate lines
|
||||||
modFile = open(moderatorsFile, "w+")
|
modFile = open(moderatorsFile, "w+")
|
||||||
|
|
@ -4767,7 +4758,6 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
if os.path.isdir(modDir):
|
if os.path.isdir(modDir):
|
||||||
setRole(baseDir,
|
setRole(baseDir,
|
||||||
modNick, domain,
|
modNick, domain,
|
||||||
'instance',
|
|
||||||
'moderator')
|
'moderator')
|
||||||
|
|
||||||
# change site editors list
|
# change site editors list
|
||||||
|
|
@ -4799,7 +4789,7 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
if os.path.isdir(edDir):
|
if os.path.isdir(edDir):
|
||||||
setRole(baseDir,
|
setRole(baseDir,
|
||||||
edNick, domain,
|
edNick, domain,
|
||||||
'instance', 'editor')
|
'editor')
|
||||||
else:
|
else:
|
||||||
# nicknames on separate lines
|
# nicknames on separate lines
|
||||||
edFile = open(editorsFile, "w+")
|
edFile = open(editorsFile, "w+")
|
||||||
|
|
@ -4824,7 +4814,6 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
if os.path.isdir(edDir):
|
if os.path.isdir(edDir):
|
||||||
setRole(baseDir,
|
setRole(baseDir,
|
||||||
edNick, domain,
|
edNick, domain,
|
||||||
'instance',
|
|
||||||
'editor')
|
'editor')
|
||||||
|
|
||||||
# change site counselors list
|
# change site counselors list
|
||||||
|
|
@ -4856,7 +4845,7 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
if os.path.isdir(edDir):
|
if os.path.isdir(edDir):
|
||||||
setRole(baseDir,
|
setRole(baseDir,
|
||||||
edNick, domain,
|
edNick, domain,
|
||||||
'instance', 'counselor')
|
'counselor')
|
||||||
else:
|
else:
|
||||||
# nicknames on separate lines
|
# nicknames on separate lines
|
||||||
edFile = open(counselorsFile, "w+")
|
edFile = open(counselorsFile, "w+")
|
||||||
|
|
@ -4881,7 +4870,6 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
if os.path.isdir(edDir):
|
if os.path.isdir(edDir):
|
||||||
setRole(baseDir,
|
setRole(baseDir,
|
||||||
edNick, domain,
|
edNick, domain,
|
||||||
'instance',
|
|
||||||
'counselor')
|
'counselor')
|
||||||
|
|
||||||
# remove scheduled posts
|
# remove scheduled posts
|
||||||
|
|
@ -7386,7 +7374,7 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
if not actorJson:
|
if not actorJson:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if actorJson.get('roles'):
|
if actorJson.get('affiliation'):
|
||||||
if self._requestHTTP():
|
if self._requestHTTP():
|
||||||
getPerson = \
|
getPerson = \
|
||||||
personLookup(domain, path.replace('/roles', ''),
|
personLookup(domain, path.replace('/roles', ''),
|
||||||
|
|
@ -7407,6 +7395,10 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
if self.server.keyShortcuts.get(nickname):
|
if self.server.keyShortcuts.get(nickname):
|
||||||
accessKeys = self.server.keyShortcuts[nickname]
|
accessKeys = self.server.keyShortcuts[nickname]
|
||||||
|
|
||||||
|
rolesList = []
|
||||||
|
if actorJson.get('affiliation'):
|
||||||
|
actorRolesStr = actorJson['affiliation']['roleName']
|
||||||
|
rolesList = getRolesFromString(actorRolesStr)
|
||||||
msg = \
|
msg = \
|
||||||
htmlProfile(self.server.rssIconAtTop,
|
htmlProfile(self.server.rssIconAtTop,
|
||||||
self.server.cssCache,
|
self.server.cssCache,
|
||||||
|
|
@ -7430,8 +7422,7 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
self.server.allowLocalNetworkAccess,
|
self.server.allowLocalNetworkAccess,
|
||||||
self.server.textModeBanner,
|
self.server.textModeBanner,
|
||||||
self.server.debug,
|
self.server.debug,
|
||||||
accessKeys,
|
accessKeys, rolesList,
|
||||||
actorJson['roles'],
|
|
||||||
None, None)
|
None, None)
|
||||||
msg = msg.encode('utf-8')
|
msg = msg.encode('utf-8')
|
||||||
msglen = len(msg)
|
msglen = len(msg)
|
||||||
|
|
@ -7443,7 +7434,12 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
'show roles')
|
'show roles')
|
||||||
else:
|
else:
|
||||||
if self._fetchAuthenticated():
|
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)
|
ensure_ascii=False)
|
||||||
msg = msg.encode('utf-8')
|
msg = msg.encode('utf-8')
|
||||||
msglen = len(msg)
|
msglen = len(msg)
|
||||||
|
|
@ -7476,7 +7472,7 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
if os.path.isfile(actorFilename):
|
if os.path.isfile(actorFilename):
|
||||||
actorJson = loadJson(actorFilename)
|
actorJson = loadJson(actorFilename)
|
||||||
if actorJson:
|
if actorJson:
|
||||||
if actorJson.get('skills'):
|
if noOfActorSkills(actorJson) > 0:
|
||||||
if self._requestHTTP():
|
if self._requestHTTP():
|
||||||
getPerson = \
|
getPerson = \
|
||||||
personLookup(domain,
|
personLookup(domain,
|
||||||
|
|
@ -7501,6 +7497,9 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
if self.server.keyShortcuts.get(nickname):
|
if self.server.keyShortcuts.get(nickname):
|
||||||
accessKeys = \
|
accessKeys = \
|
||||||
self.server.keyShortcuts[nickname]
|
self.server.keyShortcuts[nickname]
|
||||||
|
actorSkillsStr = \
|
||||||
|
actorJson['hasOccupation']['skills']
|
||||||
|
skills = getSkillsFromString(actorSkillsStr)
|
||||||
msg = \
|
msg = \
|
||||||
htmlProfile(self.server.rssIconAtTop,
|
htmlProfile(self.server.rssIconAtTop,
|
||||||
self.server.cssCache,
|
self.server.cssCache,
|
||||||
|
|
@ -7524,8 +7523,7 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
allowLocalNetworkAccess,
|
allowLocalNetworkAccess,
|
||||||
self.server.textModeBanner,
|
self.server.textModeBanner,
|
||||||
self.server.debug,
|
self.server.debug,
|
||||||
accessKeys,
|
accessKeys, skills,
|
||||||
actorJson['skills'],
|
|
||||||
None, None)
|
None, None)
|
||||||
msg = msg.encode('utf-8')
|
msg = msg.encode('utf-8')
|
||||||
msglen = len(msg)
|
msglen = len(msg)
|
||||||
|
|
@ -7538,7 +7536,10 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
'show skills')
|
'show skills')
|
||||||
else:
|
else:
|
||||||
if self._fetchAuthenticated():
|
if self._fetchAuthenticated():
|
||||||
msg = json.dumps(actorJson['skills'],
|
actorSkillsStr = \
|
||||||
|
actorJson['hasOccupation']['skills']
|
||||||
|
skills = getSkillsFromString(actorSkillsStr)
|
||||||
|
msg = json.dumps(skills,
|
||||||
ensure_ascii=False)
|
ensure_ascii=False)
|
||||||
msg = msg.encode('utf-8')
|
msg = msg.encode('utf-8')
|
||||||
msglen = len(msg)
|
msglen = len(msg)
|
||||||
|
|
@ -10571,7 +10572,11 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
msg = \
|
msg = \
|
||||||
htmlLogin(self.server.cssCache,
|
htmlLogin(self.server.cssCache,
|
||||||
self.server.translate,
|
self.server.translate,
|
||||||
self.server.baseDir, False).encode('utf-8')
|
self.server.baseDir,
|
||||||
|
self.server.httpPrefix,
|
||||||
|
self.server.domainFull,
|
||||||
|
self.server.systemLanguage,
|
||||||
|
False).encode('utf-8')
|
||||||
msglen = len(msg)
|
msglen = len(msg)
|
||||||
self._logout_headers('text/html', msglen, callingDomain)
|
self._logout_headers('text/html', msglen, callingDomain)
|
||||||
self._write(msg)
|
self._write(msg)
|
||||||
|
|
@ -11201,13 +11206,15 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
htmlAbout(self.server.cssCache,
|
htmlAbout(self.server.cssCache,
|
||||||
self.server.baseDir, 'http',
|
self.server.baseDir, 'http',
|
||||||
self.server.onionDomain,
|
self.server.onionDomain,
|
||||||
None, self.server.translate)
|
None, self.server.translate,
|
||||||
|
self.server.systemLanguage)
|
||||||
elif callingDomain.endswith('.i2p'):
|
elif callingDomain.endswith('.i2p'):
|
||||||
msg = \
|
msg = \
|
||||||
htmlAbout(self.server.cssCache,
|
htmlAbout(self.server.cssCache,
|
||||||
self.server.baseDir, 'http',
|
self.server.baseDir, 'http',
|
||||||
self.server.i2pDomain,
|
self.server.i2pDomain,
|
||||||
None, self.server.translate)
|
None, self.server.translate,
|
||||||
|
self.server.systemLanguage)
|
||||||
else:
|
else:
|
||||||
msg = \
|
msg = \
|
||||||
htmlAbout(self.server.cssCache,
|
htmlAbout(self.server.cssCache,
|
||||||
|
|
@ -11215,7 +11222,8 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
self.server.httpPrefix,
|
self.server.httpPrefix,
|
||||||
self.server.domainFull,
|
self.server.domainFull,
|
||||||
self.server.onionDomain,
|
self.server.onionDomain,
|
||||||
self.server.translate)
|
self.server.translate,
|
||||||
|
self.server.systemLanguage)
|
||||||
msg = msg.encode('utf-8')
|
msg = msg.encode('utf-8')
|
||||||
msglen = len(msg)
|
msglen = len(msg)
|
||||||
self._login_headers('text/html', msglen, callingDomain)
|
self._login_headers('text/html', msglen, callingDomain)
|
||||||
|
|
@ -11676,7 +11684,10 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
# request basic auth
|
# request basic auth
|
||||||
msg = htmlLogin(self.server.cssCache,
|
msg = htmlLogin(self.server.cssCache,
|
||||||
self.server.translate,
|
self.server.translate,
|
||||||
self.server.baseDir).encode('utf-8')
|
self.server.baseDir,
|
||||||
|
self.server.httpPrefix,
|
||||||
|
self.server.domainFull,
|
||||||
|
self.server.systemLanguage).encode('utf-8')
|
||||||
msglen = len(msg)
|
msglen = len(msg)
|
||||||
self._login_headers('text/html', msglen, callingDomain)
|
self._login_headers('text/html', msglen, callingDomain)
|
||||||
self._write(msg)
|
self._write(msg)
|
||||||
|
|
|
||||||
111
epicyon.py
111
epicyon.py
|
|
@ -74,7 +74,6 @@ from media import getAttachmentMediaType
|
||||||
from delete import sendDeleteViaServer
|
from delete import sendDeleteViaServer
|
||||||
from like import sendLikeViaServer
|
from like import sendLikeViaServer
|
||||||
from like import sendUndoLikeViaServer
|
from like import sendUndoLikeViaServer
|
||||||
from roles import sendRoleViaServer
|
|
||||||
from skills import sendSkillViaServer
|
from skills import sendSkillViaServer
|
||||||
from availability import setAvailability
|
from availability import setAvailability
|
||||||
from availability import sendAvailabilityViaServer
|
from availability import sendAvailabilityViaServer
|
||||||
|
|
@ -495,9 +494,6 @@ parser.add_argument('--maxEmoji', '--maxemoji', dest='maxEmoji',
|
||||||
help='Maximum number of emoji within a post')
|
help='Maximum number of emoji within a post')
|
||||||
parser.add_argument('--role', dest='role', type=str, default=None,
|
parser.add_argument('--role', dest='role', type=str, default=None,
|
||||||
help='Set a role for a person')
|
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,
|
parser.add_argument('--skill', dest='skill', type=str, default=None,
|
||||||
help='Set a skill for a person')
|
help='Set a skill for a person')
|
||||||
parser.add_argument('--level', dest='skillLevelPercent', type=int,
|
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')
|
help='Mute a particular post URL')
|
||||||
parser.add_argument('--unmute', dest='unmute', type=str, default=None,
|
parser.add_argument('--unmute', dest='unmute', type=str, default=None,
|
||||||
help='Unmute a particular post URL')
|
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,
|
parser.add_argument('--filter', dest='filterStr', type=str, default=None,
|
||||||
help='Adds a word or phrase which if present will ' +
|
help='Adds a word or phrase which if present will ' +
|
||||||
'cause a message to be ignored')
|
'cause a message to be ignored')
|
||||||
|
|
@ -1987,24 +1978,6 @@ if args.backgroundImage:
|
||||||
print('Background image was not added for ' + args.nickname)
|
print('Background image was not added for ' + args.nickname)
|
||||||
sys.exit()
|
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 args.skill:
|
||||||
if not nickname:
|
if not nickname:
|
||||||
print('Specify a nickname with the --nickname option')
|
print('Specify a nickname with the --nickname option')
|
||||||
|
|
@ -2218,86 +2191,6 @@ if args.unmute:
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
sys.exit()
|
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 args.unblock:
|
||||||
if not nickname:
|
if not nickname:
|
||||||
print('Specify a nickname with the --nickname option')
|
print('Specify a nickname with the --nickname option')
|
||||||
|
|
@ -2388,9 +2281,7 @@ if args.testdata:
|
||||||
True, False, 'likewhateveryouwantscoob')
|
True, False, 'likewhateveryouwantscoob')
|
||||||
setSkillLevel(baseDir, nickname, domain, 'testing', 60)
|
setSkillLevel(baseDir, nickname, domain, 'testing', 60)
|
||||||
setSkillLevel(baseDir, nickname, domain, 'typing', 50)
|
setSkillLevel(baseDir, nickname, domain, 'typing', 50)
|
||||||
setRole(baseDir, nickname, domain, 'instance', 'admin')
|
setRole(baseDir, nickname, domain, 'admin')
|
||||||
setRole(baseDir, nickname, domain, 'epicyon', 'hacker')
|
|
||||||
setRole(baseDir, nickname, domain, 'someproject', 'assistant')
|
|
||||||
setAvailability(baseDir, nickname, domain, 'busy')
|
setAvailability(baseDir, nickname, domain, 'busy')
|
||||||
|
|
||||||
addShare(baseDir,
|
addShare(baseDir,
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,6 @@ from inbox import inboxUpdateIndex
|
||||||
from announce import outboxAnnounce
|
from announce import outboxAnnounce
|
||||||
from announce import outboxUndoAnnounce
|
from announce import outboxUndoAnnounce
|
||||||
from follow import outboxUndoFollow
|
from follow import outboxUndoFollow
|
||||||
from roles import outboxDelegate
|
|
||||||
from skills import outboxSkills
|
from skills import outboxSkills
|
||||||
from availability import outboxAvailability
|
from availability import outboxAvailability
|
||||||
from like import outboxLike
|
from like import outboxLike
|
||||||
|
|
@ -313,7 +312,7 @@ def postMessageToOutbox(session, translate: {},
|
||||||
|
|
||||||
permittedOutboxTypes = ('Create', 'Announce', 'Like', 'Follow', 'Undo',
|
permittedOutboxTypes = ('Create', 'Announce', 'Like', 'Follow', 'Undo',
|
||||||
'Update', 'Add', 'Remove', 'Block', 'Delete',
|
'Update', 'Add', 'Remove', 'Block', 'Delete',
|
||||||
'Delegate', 'Skill', 'Add', 'Remove', 'Event',
|
'Skill', 'Add', 'Remove', 'Event',
|
||||||
'Ignore')
|
'Ignore')
|
||||||
if messageJson['type'] not in permittedOutboxTypes:
|
if messageJson['type'] not in permittedOutboxTypes:
|
||||||
if debug:
|
if debug:
|
||||||
|
|
@ -461,10 +460,6 @@ def postMessageToOutbox(session, translate: {},
|
||||||
print('DEBUG: handle any unfollow requests')
|
print('DEBUG: handle any unfollow requests')
|
||||||
outboxUndoFollow(baseDir, messageJson, debug)
|
outboxUndoFollow(baseDir, messageJson, debug)
|
||||||
|
|
||||||
if debug:
|
|
||||||
print('DEBUG: handle delegation requests')
|
|
||||||
outboxDelegate(baseDir, postToNickname, messageJson, debug)
|
|
||||||
|
|
||||||
if debug:
|
if debug:
|
||||||
print('DEBUG: handle skills changes requests')
|
print('DEBUG: handle skills changes requests')
|
||||||
outboxSkills(baseDir, postToNickname, messageJson, debug)
|
outboxSkills(baseDir, postToNickname, messageJson, debug)
|
||||||
|
|
|
||||||
118
person.py
118
person.py
|
|
@ -141,26 +141,6 @@ def setProfileImage(baseDir: str, httpPrefix: str, nickname: str, domain: str,
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def setOrganizationScheme(baseDir: str, nickname: str, domain: str,
|
|
||||||
schema: str) -> bool:
|
|
||||||
"""Set the organization schema within which a person exists
|
|
||||||
This will define how roles, skills and availability are assembled
|
|
||||||
into organizations
|
|
||||||
"""
|
|
||||||
# avoid giant strings
|
|
||||||
if len(schema) > 256:
|
|
||||||
return False
|
|
||||||
actorFilename = baseDir + '/accounts/' + nickname + '@' + domain + '.json'
|
|
||||||
if not os.path.isfile(actorFilename):
|
|
||||||
return False
|
|
||||||
|
|
||||||
actorJson = loadJson(actorFilename)
|
|
||||||
if actorJson:
|
|
||||||
actorJson['orgSchema'] = schema
|
|
||||||
saveJson(actorJson, actorFilename)
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def _accountExists(baseDir: str, nickname: str, domain: str) -> bool:
|
def _accountExists(baseDir: str, nickname: str, domain: str) -> bool:
|
||||||
"""Returns true if the given account exists
|
"""Returns true if the given account exists
|
||||||
"""
|
"""
|
||||||
|
|
@ -219,7 +199,10 @@ def getDefaultPersonContext() -> str:
|
||||||
'schema': 'http://schema.org#',
|
'schema': 'http://schema.org#',
|
||||||
'suspended': 'toot:suspended',
|
'suspended': 'toot:suspended',
|
||||||
'toot': 'http://joinmastodon.org/ns#',
|
'toot': 'http://joinmastodon.org/ns#',
|
||||||
'value': 'schema:value'
|
'value': 'schema:value',
|
||||||
|
'Occupation': 'schema:Occupation',
|
||||||
|
'OrganizationRole': 'schema:OrganizationRole',
|
||||||
|
'WebSite': 'schema:Project'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -295,10 +278,20 @@ def _createPersonBase(baseDir: str, nickname: str, domain: str, port: int,
|
||||||
'following': personId + '/following',
|
'following': personId + '/following',
|
||||||
'tts': personId + '/speaker',
|
'tts': personId + '/speaker',
|
||||||
'shares': personId + '/shares',
|
'shares': personId + '/shares',
|
||||||
'orgSchema': None,
|
'hasOccupation': {
|
||||||
'occupation': "",
|
'@type': 'Occupation',
|
||||||
'skills': {},
|
'name': "",
|
||||||
'roles': {},
|
'skills': ""
|
||||||
|
},
|
||||||
|
"affiliation": {
|
||||||
|
"@type": "OrganizationRole",
|
||||||
|
"roleName": "",
|
||||||
|
"affiliation": {
|
||||||
|
"@type": "WebSite",
|
||||||
|
"url": httpPrefix + '://' + domain
|
||||||
|
},
|
||||||
|
"startDate": published
|
||||||
|
},
|
||||||
'availability': None,
|
'availability': None,
|
||||||
'icon': {
|
'icon': {
|
||||||
'mediaType': 'image/png',
|
'mediaType': 'image/png',
|
||||||
|
|
@ -333,9 +326,11 @@ def _createPersonBase(baseDir: str, nickname: str, domain: str, port: int,
|
||||||
del newPerson['outbox']
|
del newPerson['outbox']
|
||||||
del newPerson['icon']
|
del newPerson['icon']
|
||||||
del newPerson['image']
|
del newPerson['image']
|
||||||
del newPerson['skills']
|
if newPerson.get('skills'):
|
||||||
|
del newPerson['skills']
|
||||||
del newPerson['shares']
|
del newPerson['shares']
|
||||||
del newPerson['roles']
|
if newPerson.get('roles'):
|
||||||
|
del newPerson['roles']
|
||||||
del newPerson['tag']
|
del newPerson['tag']
|
||||||
del newPerson['availability']
|
del newPerson['availability']
|
||||||
del newPerson['followers']
|
del newPerson['followers']
|
||||||
|
|
@ -479,10 +474,9 @@ def createPerson(baseDir: str, nickname: str, domain: str, port: int,
|
||||||
if nickname != 'news':
|
if nickname != 'news':
|
||||||
# print(nickname+' becomes the instance admin and a moderator')
|
# print(nickname+' becomes the instance admin and a moderator')
|
||||||
setConfigParam(baseDir, 'admin', nickname)
|
setConfigParam(baseDir, 'admin', nickname)
|
||||||
setRole(baseDir, nickname, domain, 'instance', 'admin')
|
setRole(baseDir, nickname, domain, 'admin')
|
||||||
setRole(baseDir, nickname, domain, 'instance', 'moderator')
|
setRole(baseDir, nickname, domain, 'moderator')
|
||||||
setRole(baseDir, nickname, domain, 'instance', 'editor')
|
setRole(baseDir, nickname, domain, 'editor')
|
||||||
setRole(baseDir, nickname, domain, 'instance', 'delegator')
|
|
||||||
|
|
||||||
if not os.path.isdir(baseDir + '/accounts'):
|
if not os.path.isdir(baseDir + '/accounts'):
|
||||||
os.mkdir(baseDir + '/accounts')
|
os.mkdir(baseDir + '/accounts')
|
||||||
|
|
@ -578,7 +572,69 @@ def personUpgradeActor(baseDir: str, personJson: {},
|
||||||
personJson['published'] = published
|
personJson['published'] = published
|
||||||
updateActor = True
|
updateActor = True
|
||||||
|
|
||||||
|
occupationName = ''
|
||||||
|
if personJson.get('occupationName'):
|
||||||
|
occupationName = personJson['occupationName']
|
||||||
|
del personJson['occupationName']
|
||||||
|
if personJson.get('occupation'):
|
||||||
|
occupationName = personJson['occupation']
|
||||||
|
del personJson['occupation']
|
||||||
|
|
||||||
|
# if the older skills format is being used then switch
|
||||||
|
# to the new one
|
||||||
|
if not personJson.get('hasOccupation'):
|
||||||
|
personJson['hasOccupation'] = {
|
||||||
|
'@type': 'Occupation',
|
||||||
|
'name': occupationName,
|
||||||
|
'skills': ""
|
||||||
|
}
|
||||||
|
updateActor = True
|
||||||
|
|
||||||
|
# remove the old skills format
|
||||||
|
if personJson.get('skills'):
|
||||||
|
del personJson['skills']
|
||||||
|
updateActor = True
|
||||||
|
|
||||||
|
# if the older roles format is being used then switch
|
||||||
|
# to the new one
|
||||||
|
if not personJson.get('affiliation'):
|
||||||
|
rolesStr = ''
|
||||||
|
adminName = getConfigParam(baseDir, 'admin')
|
||||||
|
if personJson['id'].endswith('/users/' + adminName):
|
||||||
|
rolesStr = 'admin, moderator, editor'
|
||||||
|
statusNumber, published = getStatusNumber()
|
||||||
|
personJson['affiliation'] = {
|
||||||
|
"@type": "OrganizationRole",
|
||||||
|
"roleName": rolesStr,
|
||||||
|
"affiliation": {
|
||||||
|
"@type": "WebSite",
|
||||||
|
"url": personJson['id'].split('/users/')[0]
|
||||||
|
},
|
||||||
|
"startDate": published
|
||||||
|
}
|
||||||
|
updateActor = True
|
||||||
|
|
||||||
|
# if no roles are defined then ensure that the admin
|
||||||
|
# roles are configured
|
||||||
|
if not personJson['affiliation']['roleName']:
|
||||||
|
adminName = getConfigParam(baseDir, 'admin')
|
||||||
|
if personJson['id'].endswith('/users/' + adminName):
|
||||||
|
personJson['affiliation']['roleName'] = \
|
||||||
|
'admin, moderator, editor'
|
||||||
|
updateActor = True
|
||||||
|
|
||||||
|
# remove the old roles format
|
||||||
|
if personJson.get('roles'):
|
||||||
|
del personJson['roles']
|
||||||
|
updateActor = True
|
||||||
|
|
||||||
if updateActor:
|
if updateActor:
|
||||||
|
personJson['@context'] = [
|
||||||
|
'https://www.w3.org/ns/activitystreams',
|
||||||
|
'https://w3id.org/security/v1',
|
||||||
|
getDefaultPersonContext()
|
||||||
|
],
|
||||||
|
|
||||||
saveJson(personJson, filename)
|
saveJson(personJson, filename)
|
||||||
|
|
||||||
# also update the actor within the cache
|
# also update the actor within the cache
|
||||||
|
|
|
||||||
288
roles.py
288
roles.py
|
|
@ -7,13 +7,6 @@ __email__ = "bob@freedombone.net"
|
||||||
__status__ = "Production"
|
__status__ = "Production"
|
||||||
|
|
||||||
import os
|
import os
|
||||||
from webfinger import webfingerHandle
|
|
||||||
from auth import createBasicAuthHeader
|
|
||||||
from posts import getPersonBox
|
|
||||||
from session import postJson
|
|
||||||
from utils import getFullDomain
|
|
||||||
from utils import getNicknameFromActor
|
|
||||||
from utils import getDomainFromActor
|
|
||||||
from utils import loadJson
|
from utils import loadJson
|
||||||
from utils import saveJson
|
from utils import saveJson
|
||||||
|
|
||||||
|
|
@ -37,10 +30,13 @@ def _clearRoleStatus(baseDir: str, role: str) -> None:
|
||||||
actorJson = loadJson(filename)
|
actorJson = loadJson(filename)
|
||||||
if not actorJson:
|
if not actorJson:
|
||||||
continue
|
continue
|
||||||
if actorJson['roles'].get('instance'):
|
if not actorJson.get('affiliation'):
|
||||||
if role in actorJson['roles']['instance']:
|
continue
|
||||||
actorJson['roles']['instance'].remove(role)
|
rolesList = \
|
||||||
saveJson(actorJson, filename)
|
getRolesFromString(actorJson['affiliation']['roleName'])
|
||||||
|
if role in rolesList:
|
||||||
|
rolesList.remove(role)
|
||||||
|
saveJson(actorJson, filename)
|
||||||
|
|
||||||
|
|
||||||
def clearEditorStatus(baseDir: str) -> None:
|
def clearEditorStatus(baseDir: str) -> None:
|
||||||
|
|
@ -112,13 +108,35 @@ def _removeRole(baseDir: str, nickname: str, roleFilename: str) -> None:
|
||||||
f.write(roleNickname + '\n')
|
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,
|
def setRole(baseDir: str, nickname: str, domain: str,
|
||||||
project: str, role: str) -> bool:
|
role: str) -> bool:
|
||||||
"""Set a person's role within a project
|
"""Set a person's role
|
||||||
Setting the role to an empty string or None will remove it
|
Setting the role to an empty string or None will remove it
|
||||||
"""
|
"""
|
||||||
# avoid giant strings
|
# avoid giant strings
|
||||||
if len(role) > 128 or len(project) > 128:
|
if len(role) > 128:
|
||||||
return False
|
return False
|
||||||
actorFilename = baseDir + '/accounts/' + \
|
actorFilename = baseDir + '/accounts/' + \
|
||||||
nickname + '@' + domain + '.json'
|
nickname + '@' + domain + '.json'
|
||||||
|
|
@ -133,230 +151,28 @@ def setRole(baseDir: str, nickname: str, domain: str,
|
||||||
|
|
||||||
actorJson = loadJson(actorFilename)
|
actorJson = loadJson(actorFilename)
|
||||||
if actorJson:
|
if actorJson:
|
||||||
|
if not actorJson.get('affiliation'):
|
||||||
|
return False
|
||||||
|
rolesList = \
|
||||||
|
getRolesFromString(actorJson['affiliation']['roleName'])
|
||||||
|
actorChanged = False
|
||||||
if role:
|
if role:
|
||||||
# add the role
|
# add the role
|
||||||
if project == 'instance':
|
if roleFiles.get(role):
|
||||||
if roleFiles.get(role):
|
_addRole(baseDir, nickname, domain, roleFiles[role])
|
||||||
_addRole(baseDir, nickname, domain, roleFiles[role])
|
if role not in rolesList:
|
||||||
if actorJson['roles'].get(project):
|
rolesList.append(role)
|
||||||
if role not in actorJson['roles'][project]:
|
rolesList.sort()
|
||||||
actorJson['roles'][project].append(role)
|
setRolesFromList(actorJson, rolesList)
|
||||||
else:
|
actorChanged = True
|
||||||
actorJson['roles'][project] = [role]
|
|
||||||
else:
|
else:
|
||||||
# remove the role
|
# remove the role
|
||||||
if project == 'instance':
|
if roleFiles.get(role):
|
||||||
if roleFiles.get(role):
|
_removeRole(baseDir, nickname, roleFiles[role])
|
||||||
_removeRole(baseDir, nickname, roleFiles[role])
|
if role in rolesList:
|
||||||
if actorJson['roles'].get(project):
|
rolesList.remove(role)
|
||||||
actorJson['roles'][project].remove(role)
|
setRolesFromList(actorJson, rolesList)
|
||||||
# if the project contains no roles then remove it
|
actorChanged = True
|
||||||
if len(actorJson['roles'][project]) == 0:
|
if actorChanged:
|
||||||
del actorJson['roles'][project]
|
saveJson(actorJson, actorFilename)
|
||||||
saveJson(actorJson, actorFilename)
|
|
||||||
return True
|
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
|
|
||||||
|
|
|
||||||
107
skills.py
107
skills.py
|
|
@ -15,7 +15,95 @@ from utils import getFullDomain
|
||||||
from utils import getNicknameFromActor
|
from utils import getNicknameFromActor
|
||||||
from utils import getDomainFromActor
|
from utils import getDomainFromActor
|
||||||
from utils import loadJson
|
from utils import loadJson
|
||||||
from utils import saveJson
|
|
||||||
|
|
||||||
|
def setSkillsFromDict(actorJson: {}, skillsDict: {}) -> str:
|
||||||
|
"""Converts a dict containing skills to a string
|
||||||
|
Returns the string version of the dictionary
|
||||||
|
"""
|
||||||
|
skillsStr = ''
|
||||||
|
for name, value in skillsDict.items():
|
||||||
|
if skillsStr:
|
||||||
|
skillsStr += ', '
|
||||||
|
skillsStr += name + ':' + str(value)
|
||||||
|
actorJson['hasOccupation']['skills'] = skillsStr
|
||||||
|
return skillsStr
|
||||||
|
|
||||||
|
|
||||||
|
def getSkillsFromString(skillsStr: str) -> {}:
|
||||||
|
"""Returns a dict of skills from a string
|
||||||
|
"""
|
||||||
|
skillsList = skillsStr.split(',')
|
||||||
|
skillsDict = {}
|
||||||
|
for skill in skillsList:
|
||||||
|
if ':' not in skill:
|
||||||
|
continue
|
||||||
|
name = skill.split(':')[0].strip().lower()
|
||||||
|
valueStr = skill.split(':')[1]
|
||||||
|
if not valueStr.isdigit():
|
||||||
|
continue
|
||||||
|
skillsDict[name] = int(valueStr)
|
||||||
|
return skillsDict
|
||||||
|
|
||||||
|
|
||||||
|
def actorHasSkill(actorJson: {}, skillName: str) -> bool:
|
||||||
|
"""Returns true if the actor has the given skill
|
||||||
|
"""
|
||||||
|
skillsDict = \
|
||||||
|
getSkillsFromString(actorJson['hasOccupation']['skills'])
|
||||||
|
if not skillsDict:
|
||||||
|
return False
|
||||||
|
return skillsDict.get(skillName.lower())
|
||||||
|
|
||||||
|
|
||||||
|
def actorSkillValue(actorJson: {}, skillName: str) -> int:
|
||||||
|
"""Returns The skill level from an actor
|
||||||
|
"""
|
||||||
|
skillsDict = \
|
||||||
|
getSkillsFromString(actorJson['hasOccupation']['skills'])
|
||||||
|
if not skillsDict:
|
||||||
|
return 0
|
||||||
|
skillName = skillName.lower()
|
||||||
|
if skillsDict.get(skillName):
|
||||||
|
return skillsDict[skillName]
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
def noOfActorSkills(actorJson: {}) -> int:
|
||||||
|
"""Returns the number of skills that an actor has
|
||||||
|
"""
|
||||||
|
if actorJson.get('hasOccupation'):
|
||||||
|
skillsList = actorJson['hasOccupation']['skills'].split(',')
|
||||||
|
if skillsList:
|
||||||
|
return len(skillsList)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
def setActorSkillLevel(actorJson: {},
|
||||||
|
skill: str, skillLevelPercent: int) -> bool:
|
||||||
|
"""Set a skill level for a person
|
||||||
|
Setting skill level to zero removes it
|
||||||
|
"""
|
||||||
|
if skillLevelPercent < 0 or skillLevelPercent > 100:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not actorJson:
|
||||||
|
return True
|
||||||
|
if not actorJson.get('hasOccupation'):
|
||||||
|
actorJson['hasOccupation'] = {
|
||||||
|
'@type': 'Occupation',
|
||||||
|
'name': '',
|
||||||
|
'skills': ''
|
||||||
|
}
|
||||||
|
skillsDict = \
|
||||||
|
getSkillsFromString(actorJson['hasOccupation']['skills'])
|
||||||
|
if skillLevelPercent > 0:
|
||||||
|
skillsDict[skill] = skillLevelPercent
|
||||||
|
else:
|
||||||
|
if skillsDict.get(skill):
|
||||||
|
del skillsDict[skill]
|
||||||
|
setSkillsFromDict(actorJson, skillsDict)
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
def setSkillLevel(baseDir: str, nickname: str, domain: str,
|
def setSkillLevel(baseDir: str, nickname: str, domain: str,
|
||||||
|
|
@ -30,15 +118,8 @@ def setSkillLevel(baseDir: str, nickname: str, domain: str,
|
||||||
return False
|
return False
|
||||||
|
|
||||||
actorJson = loadJson(actorFilename)
|
actorJson = loadJson(actorFilename)
|
||||||
if actorJson:
|
return setActorSkillLevel(actorJson,
|
||||||
if not actorJson.get('skills'):
|
skill, skillLevelPercent)
|
||||||
actorJson['skills'] = {}
|
|
||||||
if skillLevelPercent > 0:
|
|
||||||
actorJson['skills'][skill] = skillLevelPercent
|
|
||||||
else:
|
|
||||||
del actorJson['skills'][skill]
|
|
||||||
saveJson(actorJson, actorFilename)
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def getSkills(baseDir: str, nickname: str, domain: str) -> []:
|
def getSkills(baseDir: str, nickname: str, domain: str) -> []:
|
||||||
|
|
@ -50,9 +131,9 @@ def getSkills(baseDir: str, nickname: str, domain: str) -> []:
|
||||||
|
|
||||||
actorJson = loadJson(actorFilename)
|
actorJson = loadJson(actorFilename)
|
||||||
if actorJson:
|
if actorJson:
|
||||||
if not actorJson.get('skills'):
|
if not actorJson.get('hasOccupation'):
|
||||||
return None
|
return None
|
||||||
return actorJson['skills']
|
return getSkillsFromString(actorJson['hasOccupation']['skills'])
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -112,7 +193,7 @@ def sendSkillViaServer(baseDir: str, session, nickname: str, password: str,
|
||||||
newSkillJson = {
|
newSkillJson = {
|
||||||
'type': 'Skill',
|
'type': 'Skill',
|
||||||
'actor': actor,
|
'actor': actor,
|
||||||
'object': '"'+skillStr+'"',
|
'object': '"' + skillStr + '"',
|
||||||
'to': [toUrl],
|
'to': [toUrl],
|
||||||
'cc': [ccUrl]
|
'cc': [ccUrl]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
129
tests.py
129
tests.py
|
|
@ -67,8 +67,11 @@ from person import setDisplayNickname
|
||||||
from person import setBio
|
from person import setBio
|
||||||
# from person import generateRSAKey
|
# from person import generateRSAKey
|
||||||
from skills import setSkillLevel
|
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 setRole
|
||||||
from roles import outboxDelegate
|
|
||||||
from auth import constantTimeStringCheck
|
from auth import constantTimeStringCheck
|
||||||
from auth import createBasicAuthHeader
|
from auth import createBasicAuthHeader
|
||||||
from auth import authorizeBasic
|
from auth import authorizeBasic
|
||||||
|
|
@ -452,7 +455,7 @@ def createServerAlice(path: str, domain: str, port: int,
|
||||||
deleteAllPosts(path, nickname, domain, 'inbox')
|
deleteAllPosts(path, nickname, domain, 'inbox')
|
||||||
deleteAllPosts(path, nickname, domain, 'outbox')
|
deleteAllPosts(path, nickname, domain, 'outbox')
|
||||||
assert setSkillLevel(path, nickname, domain, 'hacking', 90)
|
assert setSkillLevel(path, nickname, domain, 'hacking', 90)
|
||||||
assert setRole(path, nickname, domain, 'someproject', 'guru')
|
assert setRole(path, nickname, domain, 'guru')
|
||||||
if hasFollows:
|
if hasFollows:
|
||||||
followPerson(path, nickname, domain, 'bob', bobAddress,
|
followPerson(path, nickname, domain, 'bob', bobAddress,
|
||||||
federationList, False)
|
federationList, False)
|
||||||
|
|
@ -556,8 +559,6 @@ def createServerBob(path: str, domain: str, port: int,
|
||||||
False, password)
|
False, password)
|
||||||
deleteAllPosts(path, nickname, domain, 'inbox')
|
deleteAllPosts(path, nickname, domain, 'inbox')
|
||||||
deleteAllPosts(path, nickname, domain, 'outbox')
|
deleteAllPosts(path, nickname, domain, 'outbox')
|
||||||
assert setRole(path, nickname, domain, 'bandname', 'bass player')
|
|
||||||
assert setRole(path, nickname, domain, 'bandname', 'publicist')
|
|
||||||
if hasFollows:
|
if hasFollows:
|
||||||
followPerson(path, nickname, domain,
|
followPerson(path, nickname, domain,
|
||||||
'alice', aliceAddress, federationList, False)
|
'alice', aliceAddress, federationList, False)
|
||||||
|
|
@ -1409,80 +1410,6 @@ def testCreatePerson():
|
||||||
shutil.rmtree(baseDir)
|
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():
|
def testAuthentication():
|
||||||
print('testAuthentication')
|
print('testAuthentication')
|
||||||
currDir = os.getcwd()
|
currDir = os.getcwd()
|
||||||
|
|
@ -3731,9 +3658,54 @@ def testSpoofGeolocation() -> None:
|
||||||
kmlFile.close()
|
kmlFile.close()
|
||||||
|
|
||||||
|
|
||||||
|
def testSkills() -> None:
|
||||||
|
print('testSkills')
|
||||||
|
actorJson = {
|
||||||
|
'hasOccupation': {
|
||||||
|
'@type': 'Occupation',
|
||||||
|
'name': "",
|
||||||
|
'skills': ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
skillsDict = {
|
||||||
|
'bakery': 40,
|
||||||
|
'gardening': 70
|
||||||
|
}
|
||||||
|
setSkillsFromDict(actorJson, skillsDict)
|
||||||
|
assert actorJson['hasOccupation']['skills']
|
||||||
|
skillsDict = getSkillsFromString(actorJson['hasOccupation']['skills'])
|
||||||
|
assert skillsDict.get('bakery')
|
||||||
|
assert skillsDict.get('gardening')
|
||||||
|
assert skillsDict['bakery'] == 40
|
||||||
|
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():
|
def runAllTests():
|
||||||
print('Running tests...')
|
print('Running tests...')
|
||||||
testFunctions()
|
testFunctions()
|
||||||
|
testRoles()
|
||||||
|
testSkills()
|
||||||
testSpoofGeolocation()
|
testSpoofGeolocation()
|
||||||
testRemovePostInteractions()
|
testRemovePostInteractions()
|
||||||
testExtractPGPPublicKey()
|
testExtractPGPPublicKey()
|
||||||
|
|
@ -3787,5 +3759,4 @@ def runAllTests():
|
||||||
testNoOfFollowersOnDomain()
|
testNoOfFollowersOnDomain()
|
||||||
testFollows()
|
testFollows()
|
||||||
testGroupFollowers()
|
testGroupFollowers()
|
||||||
testDelegateRoles()
|
|
||||||
print('Tests succeeded\n')
|
print('Tests succeeded\n')
|
||||||
|
|
|
||||||
|
|
@ -9,13 +9,14 @@ __status__ = "Production"
|
||||||
import os
|
import os
|
||||||
from shutil import copyfile
|
from shutil import copyfile
|
||||||
from utils import getConfigParam
|
from utils import getConfigParam
|
||||||
from webapp_utils import htmlHeaderWithExternalStyle
|
from webapp_utils import htmlHeaderWithWebsiteMarkup
|
||||||
from webapp_utils import htmlFooter
|
from webapp_utils import htmlFooter
|
||||||
from webapp_utils import markdownToHtml
|
from webapp_utils import markdownToHtml
|
||||||
|
|
||||||
|
|
||||||
def htmlAbout(cssCache: {}, baseDir: str, httpPrefix: str,
|
def htmlAbout(cssCache: {}, baseDir: str, httpPrefix: str,
|
||||||
domainFull: str, onionDomain: str, translate: {}) -> str:
|
domainFull: str, onionDomain: str, translate: {},
|
||||||
|
systemLanguage: str) -> str:
|
||||||
"""Show the about screen
|
"""Show the about screen
|
||||||
"""
|
"""
|
||||||
adminNickname = getConfigParam(baseDir, 'admin')
|
adminNickname = getConfigParam(baseDir, 'admin')
|
||||||
|
|
@ -40,7 +41,10 @@ def htmlAbout(cssCache: {}, baseDir: str, httpPrefix: str,
|
||||||
|
|
||||||
instanceTitle = \
|
instanceTitle = \
|
||||||
getConfigParam(baseDir, 'instanceTitle')
|
getConfigParam(baseDir, 'instanceTitle')
|
||||||
aboutForm = htmlHeaderWithExternalStyle(cssFilename, instanceTitle)
|
aboutForm = \
|
||||||
|
htmlHeaderWithWebsiteMarkup(cssFilename, instanceTitle,
|
||||||
|
httpPrefix, domainFull,
|
||||||
|
systemLanguage)
|
||||||
aboutForm += '<div class="container">' + aboutText + '</div>'
|
aboutForm += '<div class="container">' + aboutText + '</div>'
|
||||||
if onionDomain:
|
if onionDomain:
|
||||||
aboutForm += \
|
aboutForm += \
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ import time
|
||||||
from shutil import copyfile
|
from shutil import copyfile
|
||||||
from utils import getConfigParam
|
from utils import getConfigParam
|
||||||
from utils import noOfAccounts
|
from utils import noOfAccounts
|
||||||
from webapp_utils import htmlHeaderWithExternalStyle
|
from webapp_utils import htmlHeaderWithWebsiteMarkup
|
||||||
from webapp_utils import htmlFooter
|
from webapp_utils import htmlFooter
|
||||||
from webapp_utils import htmlKeyboardNavigation
|
from webapp_utils import htmlKeyboardNavigation
|
||||||
from theme import getTextModeLogo
|
from theme import getTextModeLogo
|
||||||
|
|
@ -51,7 +51,10 @@ def htmlGetLoginCredentials(loginParams: str,
|
||||||
|
|
||||||
|
|
||||||
def htmlLogin(cssCache: {}, translate: {},
|
def htmlLogin(cssCache: {}, translate: {},
|
||||||
baseDir: str, autocomplete=True) -> str:
|
baseDir: str,
|
||||||
|
httpPrefix: str, domain: str,
|
||||||
|
systemLanguage: str,
|
||||||
|
autocomplete=True) -> str:
|
||||||
"""Shows the login screen
|
"""Shows the login screen
|
||||||
"""
|
"""
|
||||||
accounts = noOfAccounts(baseDir)
|
accounts = noOfAccounts(baseDir)
|
||||||
|
|
@ -145,7 +148,10 @@ def htmlLogin(cssCache: {}, translate: {},
|
||||||
|
|
||||||
instanceTitle = \
|
instanceTitle = \
|
||||||
getConfigParam(baseDir, 'instanceTitle')
|
getConfigParam(baseDir, 'instanceTitle')
|
||||||
loginForm = htmlHeaderWithExternalStyle(cssFilename, instanceTitle)
|
loginForm = \
|
||||||
|
htmlHeaderWithWebsiteMarkup(cssFilename, instanceTitle,
|
||||||
|
httpPrefix, domain,
|
||||||
|
systemLanguage)
|
||||||
loginForm += '<br>\n'
|
loginForm += '<br>\n'
|
||||||
loginForm += '<form method="POST" action="/login">\n'
|
loginForm += '<form method="POST" action="/login">\n'
|
||||||
loginForm += ' <div class="imgcontainer">\n'
|
loginForm += ' <div class="imgcontainer">\n'
|
||||||
|
|
|
||||||
|
|
@ -740,8 +740,9 @@ def htmlProfile(rssIconAtTop: bool,
|
||||||
if 'T' in profileJson['published']:
|
if 'T' in profileJson['published']:
|
||||||
joinedDate = profileJson['published']
|
joinedDate = profileJson['published']
|
||||||
occupationName = None
|
occupationName = None
|
||||||
if profileJson.get('occupationName'):
|
if profileJson.get('hasOccupation'):
|
||||||
occupationName = profileJson['occupationName']
|
if profileJson['hasOccupation'].get('name'):
|
||||||
|
occupationName = profileJson['hasOccupation']['name']
|
||||||
|
|
||||||
avatarUrl = profileJson['icon']['url']
|
avatarUrl = profileJson['icon']['url']
|
||||||
|
|
||||||
|
|
@ -1026,20 +1027,18 @@ def _htmlProfileFollowing(translate: {}, baseDir: str, httpPrefix: str,
|
||||||
|
|
||||||
|
|
||||||
def _htmlProfileRoles(translate: {}, nickname: str, domain: str,
|
def _htmlProfileRoles(translate: {}, nickname: str, domain: str,
|
||||||
rolesJson: {}) -> str:
|
rolesList: []) -> str:
|
||||||
"""Shows roles on the profile screen
|
"""Shows roles on the profile screen
|
||||||
"""
|
"""
|
||||||
profileStr = ''
|
profileStr = ''
|
||||||
for project, rolesList in rolesJson.items():
|
profileStr += \
|
||||||
profileStr += \
|
'<div class="roles">\n<div class="roles-inner">\n'
|
||||||
'<div class="roles">\n<h2>' + project + \
|
for role in rolesList:
|
||||||
'</h2>\n<div class="roles-inner">\n'
|
if translate.get(role):
|
||||||
for role in rolesList:
|
profileStr += '<h3>' + translate[role] + '</h3>\n'
|
||||||
if translate.get(role):
|
else:
|
||||||
profileStr += '<h3>' + translate[role] + '</h3>\n'
|
profileStr += '<h3>' + role + '</h3>\n'
|
||||||
else:
|
profileStr += '</div></div>\n'
|
||||||
profileStr += '<h3>' + role + '</h3>\n'
|
|
||||||
profileStr += '</div></div>\n'
|
|
||||||
if len(profileStr) == 0:
|
if len(profileStr) == 0:
|
||||||
profileStr += \
|
profileStr += \
|
||||||
'<p>@' + nickname + '@' + domain + ' has no roles assigned</p>\n'
|
'<p>@' + nickname + '@' + domain + ' has no roles assigned</p>\n'
|
||||||
|
|
@ -1601,8 +1600,9 @@ def htmlEditProfile(cssCache: {}, translate: {}, baseDir: str, path: str,
|
||||||
editProfileForm += ' accept="' + imageFormats + '">\n'
|
editProfileForm += ' accept="' + imageFormats + '">\n'
|
||||||
|
|
||||||
occupationName = ''
|
occupationName = ''
|
||||||
if actorJson.get('occupationName'):
|
if actorJson.get('hasOccupation'):
|
||||||
occupationName = actorJson['occupationName']
|
if actorJson['hasOccupation'].get('name'):
|
||||||
|
occupationName = actorJson['hasOccupation']['name']
|
||||||
|
|
||||||
editProfileForm += '<label class="labels">' + \
|
editProfileForm += '<label class="labels">' + \
|
||||||
translate['Occupation'] + ':</label><br>\n'
|
translate['Occupation'] + ':</label><br>\n'
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,8 @@ from utils import locatePost
|
||||||
from utils import isPublicPost
|
from utils import isPublicPost
|
||||||
from utils import firstParagraphFromString
|
from utils import firstParagraphFromString
|
||||||
from utils import searchBoxPosts
|
from utils import searchBoxPosts
|
||||||
|
from skills import noOfActorSkills
|
||||||
|
from skills import getSkillsFromString
|
||||||
from categories import getHashtagCategory
|
from categories import getHashtagCategory
|
||||||
from feeds import rss2TagHeader
|
from feeds import rss2TagHeader
|
||||||
from feeds import rss2TagFooter
|
from feeds import rss2TagFooter
|
||||||
|
|
@ -414,11 +416,13 @@ def htmlSkillsSearch(actor: str,
|
||||||
actorJson = loadJson(actorFilename)
|
actorJson = loadJson(actorFilename)
|
||||||
if actorJson:
|
if actorJson:
|
||||||
if actorJson.get('id') and \
|
if actorJson.get('id') and \
|
||||||
actorJson.get('skills') and \
|
noOfActorSkills(actorJson) > 0 and \
|
||||||
actorJson.get('name') and \
|
actorJson.get('name') and \
|
||||||
actorJson.get('icon'):
|
actorJson.get('icon'):
|
||||||
actor = actorJson['id']
|
actor = actorJson['id']
|
||||||
for skillName, skillLevel in actorJson['skills'].items():
|
actorSkillsStr = actorJson['hasOccupation']['skills']
|
||||||
|
skills = getSkillsFromString(actorSkillsStr)
|
||||||
|
for skillName, skillLevel in skills.items():
|
||||||
skillName = skillName.lower()
|
skillName = skillName.lower()
|
||||||
if not (skillName in skillsearch or
|
if not (skillName in skillsearch or
|
||||||
skillsearch in skillName):
|
skillsearch in skillName):
|
||||||
|
|
@ -453,12 +457,14 @@ def htmlSkillsSearch(actor: str,
|
||||||
if cachedActorJson.get('actor'):
|
if cachedActorJson.get('actor'):
|
||||||
actorJson = cachedActorJson['actor']
|
actorJson = cachedActorJson['actor']
|
||||||
if actorJson.get('id') and \
|
if actorJson.get('id') and \
|
||||||
actorJson.get('skills') and \
|
noOfActorSkills(actorJson) > 0 and \
|
||||||
actorJson.get('name') and \
|
actorJson.get('name') and \
|
||||||
actorJson.get('icon'):
|
actorJson.get('icon'):
|
||||||
actor = actorJson['id']
|
actor = actorJson['id']
|
||||||
for skillName, skillLevel in \
|
actorSkillsStr = \
|
||||||
actorJson['skills'].items():
|
actorJson['hasOccupation']['skills']
|
||||||
|
skills = getSkillsFromString(actorSkillsStr)
|
||||||
|
for skillName, skillLevel in skills.items():
|
||||||
skillName = skillName.lower()
|
skillName = skillName.lower()
|
||||||
if not (skillName in skillsearch or
|
if not (skillName in skillsearch or
|
||||||
skillsearch in skillName):
|
skillsearch in skillName):
|
||||||
|
|
|
||||||
|
|
@ -720,18 +720,11 @@ def htmlHeaderWithPersonMarkup(cssFilename: str, instanceTitle: str,
|
||||||
return htmlStr
|
return htmlStr
|
||||||
|
|
||||||
skillsMarkup = ''
|
skillsMarkup = ''
|
||||||
if actorJson.get('skills'):
|
if actorJson.get('hasOccupation'):
|
||||||
skillsStr = ''
|
skillsStr = actorJson['hasOccupation']['skills']
|
||||||
for skillName, skillValue in actorJson['skills'].items():
|
if actorJson['hasOccupation'].get('name'):
|
||||||
if skillsStr:
|
occupationName = actorJson['hasOccupation']['name']
|
||||||
skillsStr += ', ' + skillName
|
occupationStr = ' "name": "' + occupationName + '",\n'
|
||||||
else:
|
|
||||||
skillsStr += skillName
|
|
||||||
if skillsStr:
|
|
||||||
occupationStr = ''
|
|
||||||
if actorJson.get('occupationName'):
|
|
||||||
occupationName = actorJson['occupationName']
|
|
||||||
occupationStr = ' "name": "' + occupationName + '",\n'
|
|
||||||
skillsMarkup = \
|
skillsMarkup = \
|
||||||
' "hasOccupation": {\n' + \
|
' "hasOccupation": {\n' + \
|
||||||
' "@type": "Occupation",\n' + \
|
' "@type": "Occupation",\n' + \
|
||||||
|
|
@ -755,6 +748,45 @@ def htmlHeaderWithPersonMarkup(cssFilename: str, instanceTitle: str,
|
||||||
return htmlStr
|
return htmlStr
|
||||||
|
|
||||||
|
|
||||||
|
def htmlHeaderWithWebsiteMarkup(cssFilename: str, instanceTitle: str,
|
||||||
|
httpPrefix: str, domain: str,
|
||||||
|
systemLanguage: str) -> str:
|
||||||
|
"""html header which includes website markup
|
||||||
|
https://schema.org/WebSite
|
||||||
|
"""
|
||||||
|
htmlStr = htmlHeaderWithExternalStyle(cssFilename, instanceTitle,
|
||||||
|
systemLanguage)
|
||||||
|
|
||||||
|
licenseUrl = 'https://www.gnu.org/licenses/agpl-3.0.en.html'
|
||||||
|
websiteMarkup = \
|
||||||
|
' <script type="application/ld+json">\n' + \
|
||||||
|
' {\n' + \
|
||||||
|
' "@context" : "http://schema.org",\n' + \
|
||||||
|
' "@type" : "WebSite",\n' + \
|
||||||
|
' "name": "' + instanceTitle + '",\n' + \
|
||||||
|
' "url": "' + httpPrefix + '://' + domain + '",\n' + \
|
||||||
|
' "license": "' + licenseUrl + '",\n' + \
|
||||||
|
' "inLanguage": "' + systemLanguage + '",\n' + \
|
||||||
|
' "isAccessibleForFree": true,\n' + \
|
||||||
|
' "genre": "https://en.wikipedia.org/wiki/Fediverse",\n' + \
|
||||||
|
' "accessMode": ["textual", "visual"],\n' + \
|
||||||
|
' "accessModeSufficient": ["textual"],\n' + \
|
||||||
|
' "accessibilityAPI" : ["ARIA"],\n' + \
|
||||||
|
' "accessibilityControl" : [\n' + \
|
||||||
|
' "fullKeyboardControl",\n' + \
|
||||||
|
' "fullTouchControl",\n' + \
|
||||||
|
' "fullMouseControl"\n' + \
|
||||||
|
' ],\n' + \
|
||||||
|
' "encodingFormat" : [\n' + \
|
||||||
|
' "text/html", "image/png", "image/webp",\n' + \
|
||||||
|
' "image/jpeg", "image/gif", "text/css"\n' + \
|
||||||
|
' ]\n' + \
|
||||||
|
' }\n' + \
|
||||||
|
' </script>\n'
|
||||||
|
htmlStr = htmlStr.replace('<head>\n', '<head>\n' + websiteMarkup)
|
||||||
|
return htmlStr
|
||||||
|
|
||||||
|
|
||||||
def htmlFooter() -> str:
|
def htmlFooter() -> str:
|
||||||
htmlStr = ' </body>\n'
|
htmlStr = ' </body>\n'
|
||||||
htmlStr += '</html>\n'
|
htmlStr += '</html>\n'
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue