Terms of service

master
Bob Mottram 2019-08-08 14:38:33 +01:00
parent 13dc4e889b
commit ed46de3709
4 changed files with 95 additions and 10 deletions

View File

@ -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)

25
default_tos.txt 100644
View File

@ -0,0 +1,25 @@
<h1>Terms of Service</h1>
<h3>Data Collected</h3>
<p>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.</p>
<p>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.</p>
<p>No IP addresses are logged.</p>
<p>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.</p>
<h3>Content Policy</h3>
<p>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.</p>
<p>Violent or abusive content will be subject to moderation and is likely to be removed.</p>
<p>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.</p>
<h3>No Commercial Use</h3>
<p>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.</p>
<p>Commercial use includes the harvesting of data to create products which are then sold, such as statistics, business reports or machine learning models.</p>

View File

@ -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

View File

@ -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<lastLoginTime+5:
return None,None
if currTime<lastLoginTime+10:
return None,None,None
if '&' not in loginParams:
return None,None
return None,None,None
loginArgs=loginParams.split('&')
nickname=None
password=None
register=False
for arg in loginArgs:
if '=' in arg:
if arg.split('=',1)[0]=='username':
nickname=arg.split('=',1)[1]
elif arg.split('=',1)[0]=='password':
password=arg.split('=',1)[1]
return nickname,password
elif arg.split('=',1)[0]=='register':
register=True
return nickname,password,register
def htmlLogin(baseDir: str) -> 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='<p class="login-text">Welcome. Please login or register a new account.</p>'
registerButtonStr='<button type="submit" name="register">Register</button>'
TOSstr='<p class="login-text"><a href="/terms">Terms of Service</a></p>'
loginButtonStr=''
if accounts>0:
loginButtonStr='<button type="submit" name="submit">Login</button>'
@ -186,7 +193,7 @@ def htmlLogin(baseDir: str) -> str:
'<form method="POST" action="/login">' \
' <div class="imgcontainer">' \
' <img src="login.png" alt="login image" class="loginimage">'+ \
loginText+ \
loginText+TOSstr+ \
' </div>' \
'' \
' <div class="container">' \
@ -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+='<div class="container">'+TOSText+'</div>'
TOSForm+=htmlFooter()
return TOSForm
def htmlNewPost(baseDir: str,path: str,inReplyTo: str,mentions: []) -> str:
replyStr=''
if not path.endswith('/newshare'):