From ed46de37099586d1ceb52c9f306fb1bcad472768 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Thu, 8 Aug 2019 14:38:33 +0100 Subject: [PATCH] Terms of service --- daemon.py | 21 ++++++++++++++++++--- default_tos.txt | 25 +++++++++++++++++++++++++ person.py | 17 +++++++++++++++++ webinterface.py | 42 +++++++++++++++++++++++++++++++++++------- 4 files changed, 95 insertions(+), 10 deletions(-) create mode 100644 default_tos.txt diff --git a/daemon.py b/daemon.py index c82599d86..1c67e8285 100644 --- a/daemon.py +++ b/daemon.py @@ -22,6 +22,7 @@ from session import createSession from webfinger import webfingerMeta from webfinger import webfingerLookup from webfinger import webfingerHandle +from person import registerAccount from person import personLookup from person import personBoxJson from person import createSharedInbox @@ -74,6 +75,7 @@ from webinterface import htmlSearch from webinterface import htmlUnfollowConfirm from webinterface import htmlProfileAfterSearch from webinterface import htmlEditProfile +from webinterface import htmlTermsOfService from shares import getSharesFeedForPerson from shares import outboxShareUpload from shares import outboxUndoShareUpload @@ -429,7 +431,7 @@ class PubServer(BaseHTTPRequestHandler): # if not authorized then show the login screen if self.headers.get('Accept'): - if 'text/html' in self.headers['Accept'] and self.path!='/login': + if 'text/html' in self.headers['Accept'] and self.path!='/login' and self.path!='/terms': if '/media/' not in self.path and \ '/sharefiles/' not in self.path and \ '/statuses/' not in self.path and \ @@ -602,7 +604,13 @@ class PubServer(BaseHTTPRequestHandler): if self._webfinger(): self.server.GETbusy=False return - + + if self.path.startswith('/terms'): + self._login_headers('text/html') + self.wfile.write(htmlTermsOfService(self.server.baseDir).encode()) + self.server.GETbusy=False + return + if self.path.startswith('/login'): # request basic auth self._login_headers('text/html') @@ -1654,9 +1662,16 @@ class PubServer(BaseHTTPRequestHandler): self.server.POSTbusy=False return loginParams=self.rfile.read(length).decode('utf-8') - loginNickname,loginPassword=htmlGetLoginCredentials(loginParams,self.server.lastLoginTime) + loginNickname,loginPassword,register=htmlGetLoginCredentials(loginParams,self.server.lastLoginTime) if loginNickname: self.server.lastLoginTime=int(time.time()) + if register: + if not registerAccount(self.server.baseDir,self.server.httpPrefix, \ + self.server.domain,self.server.port, \ + loginNickname,loginPassword): + self.server.POSTbusy=False + self._redirect_headers('/login',cookie) + return authHeader=createBasicAuthHeader(loginNickname,loginPassword) if not authorizeBasic(self.server.baseDir,'/users/'+loginNickname+'/outbox',authHeader,False): print('Login failed: '+loginNickname) diff --git a/default_tos.txt b/default_tos.txt new file mode 100644 index 000000000..bf464d6f0 --- /dev/null +++ b/default_tos.txt @@ -0,0 +1,25 @@ +

Terms of Service

+ +

Data Collected

+ +

Your username and a hash of your password, any posts you make and a list of accounts which you follow. The admin of the site does not know your password and it is not stored in plaintext anywhere.

+ +

There is a quota on the number of posts retained by this instance for each account. Older posts will be removed when the limit is reached. Anything you post here should be considered ephemeral and you should keep a separate personal copy of them if you wish to retain a permanent archive.

+ +

No IP addresses are logged.

+ +

Posts can be removed on request if there is sufficient justification, but the nature of ActivityPub means that deletion of data federated to other instances cannot be guaranteed.

+ +

Content Policy

+ +

This instance will not host content containing sexism, racism, homophobia, transphobia, misogyny, antisemitism or other forms of bigotry or discrimination on the basis of nationality. Claims that transgressions of this type were intended to be "ironic" will be treated as a terms of service violation.

+ +

Violent or abusive content will be subject to moderation and is likely to be removed.

+ +

Content found to be non-compliant with this policy will be removed and any accounts on this instance producing, repeating or linking to such content will be deleted typically without prior notification.

+ +

No Commercial Use

+ +

Commercial use of original content on this instance is strictly forbidden without the prior written permission of the account holder. Publication or federation of content does not imply permission for commercial use.

+ +

Commercial use includes the harvesting of data to create products which are then sold, such as statistics, business reports or machine learning models.

diff --git a/person.py b/person.py index ab4fba225..02dd46ab6 100644 --- a/person.py +++ b/person.py @@ -218,6 +218,23 @@ def createPersonBase(baseDir: str,nickname: str,domain: str,port: int, \ return privateKeyPem,publicKeyPem,newPerson,webfingerEndpoint +def registerAccount(baseDir: str,httpPrefix: str,domain: str,port: int, \ + nickname: str,password: str) -> bool: + """Registers a new account from the web interface + """ + if not validNickname(nickname): + print('REGISTER: Nickname '+nickname+' is invalid') + return False + if len(password)<8: + print('REGISTER: Password should be at least 8 characters') + return False + privateKeyPem,publicKeyPem,newPerson,webfingerEndpoint= \ + createPerson(baseDir,nickname,domain,port, \ + httpPrefix,True,password) + if privateKeyPem: + return True + return False + def createPerson(baseDir: str,nickname: str,domain: str,port: int, \ httpPrefix: str, saveToFile: bool,password=None) -> (str,str,{},{}): """Returns the private key, public key, actor and webfinger endpoint diff --git a/webinterface.py b/webinterface.py index a7a1d8704..c2e44a5c1 100644 --- a/webinterface.py +++ b/webinterface.py @@ -129,27 +129,30 @@ def htmlEditProfile(baseDir: str,path: str,domain: str,port: int) -> str: editProfileForm+=htmlFooter() return editProfileForm -def htmlGetLoginCredentials(loginParams: str,lastLoginTime: int) -> (str,str): +def htmlGetLoginCredentials(loginParams: str,lastLoginTime: int) -> (str,str,bool): """Receives login credentials via HTTPServer POST """ if not loginParams.startswith('username='): - return None,None + return None,None,None # minimum time between login attempts currTime=int(time.time()) - if currTime str: accounts=noOfAccounts(baseDir) @@ -175,8 +178,12 @@ def htmlLogin(baseDir: str) -> str: registerButtonStr='' if getConfigParam(baseDir,'registration')=='open': if int(getConfigParam(baseDir,'registrationsRemaining'))>0: + if accounts>0: + loginText='' registerButtonStr='' + TOSstr='' + loginButtonStr='' if accounts>0: loginButtonStr='' @@ -186,7 +193,7 @@ def htmlLogin(baseDir: str) -> str: '
' \ '
' \ ' login image'+ \ - loginText+ \ + loginText+TOSstr+ \ '
' \ '' \ '
' \ @@ -201,6 +208,27 @@ def htmlLogin(baseDir: str) -> str: loginForm+=htmlFooter() return loginForm +def htmlTermsOfService(baseDir: str) -> str: + if not os.path.isfile(baseDir+'/accounts/tos.txt'): + copyfile(baseDir+'/default_tos.txt',baseDir+'/accounts/tos.txt') + if os.path.isfile(baseDir+'/img/login-background.png'): + if not os.path.isfile(baseDir+'/accounts/login-background.png'): + copyfile(baseDir+'/img/login-background.png',baseDir+'/accounts/login-background.png') + + TOSText='Terms of Service go here.' + if os.path.isfile(baseDir+'/accounts/tos.txt'): + with open(baseDir+'/accounts/tos.txt', 'r') as file: + TOSText = file.read() + + TOSForm='' + with open(baseDir+'/epicyon-profile.css', 'r') as cssFile: + termsCSS = cssFile.read() + + TOSForm=htmlHeader(termsCSS) + TOSForm+='
'+TOSText+'
' + TOSForm+=htmlFooter() + return TOSForm + def htmlNewPost(baseDir: str,path: str,inReplyTo: str,mentions: []) -> str: replyStr='' if not path.endswith('/newshare'):