Skip to content
This repository was archived by the owner on Sep 29, 2023. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 21 additions & 10 deletions bones/bone.py
Original file line number Diff line number Diff line change
Expand Up @@ -366,16 +366,27 @@ def getUniquePropertyIndexValue( self, valuesCache, name ):
"""
Returns an hash for our current value, used to store in the uniqueProptertyValue index.
"""
if valuesCache[name] is None:
return( None )
h = hashlib.sha256()
h.update( unicode( valuesCache[name] ).encode("UTF-8") )
res = h.hexdigest()
if isinstance( valuesCache[name], int ) or isinstance( valuesCache[name], float ) or isinstance( valuesCache[name], long ):
return("I-%s" % res )
elif isinstance( valuesCache[name], str ) or isinstance( valuesCache[name], unicode ):
return("S-%s" % res )
raise NotImplementedError("Type %s can't be safely used in an uniquePropertyIndex" % type(valuesCache[name]) )
values = valuesCache[name]
if values is None:
return None
res = []
# Make value a list so we can loop over it in any case
if not isinstance(values, list):
values = [values]
for item in values:
h = hashlib.sha256()
h.update( unicode( item ).encode("UTF-8") )
# res = h.hexdigest()
if isinstance( item, int ) or isinstance( item, float ) or isinstance( item, long ):
res.append("I-%s" % h.hexdigest() )
elif isinstance( item, str ) or isinstance( item, unicode ):
res.append("S-%s" % h.hexdigest() )
else:
raise NotImplementedError("Type %s can't be safely used in an uniquePropertyIndex" % type(item) )
if isinstance(valuesCache[name], list):
return res
# Return back to original non-list
return res[0]

def getReferencedBlobs( self, valuesCache, name ):
"""
Expand Down
2 changes: 1 addition & 1 deletion bones/stringBone.py
Original file line number Diff line number Diff line change
Expand Up @@ -382,5 +382,5 @@ def getUniquePropertyIndexValue(self, valuesCache, name):
"""
if not valuesCache[name] and not self.required: #Dont enforce a unique property on an empty string if we are required=False
return( None )
return( super( stringBone, self).getUniquePropertyIndexValue(valuesCache, name))
return super( stringBone, self).getUniquePropertyIndexValue(valuesCache, name)

39 changes: 29 additions & 10 deletions modules/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,8 @@ def login(self, name=None, password=None, skey="", *args, **kwargs):

# Check if the username matches
storedUserName = res.get("name.idx", "")
if isinstance(storedUserName, list): # Multiple emails: only use main one (first in list)
storedUserName = storedUserName[0]
if len(storedUserName) != len(name.lower()):
isOkay = False
else:
Expand All @@ -176,7 +178,7 @@ def login(self, name=None, password=None, skey="", *args, **kwargs):
if not isOkay:
skel=self.loginSkel()
skel.fromClient({"name": name, "nomissing": "1"})
return self.userModule.render.login(skel, loginFailed=True)
return self.userModule.render.login(skel, params={"loginFailed": True})
else:
if not "password_salt" in res: #Update the password to the new, more secure format
res[ "password_salt" ] = utils.generateRandomString( 13 )
Expand All @@ -202,7 +204,10 @@ def pwrecover( self, authtoken=None, skey=None, *args, **kwargs ):
skel = self.lostPasswordSkel()
if len(kwargs)==0 or not skel.fromClient(kwargs) or not securitykey.validate(skey):
return self.userModule.render.passwdRecover(skel, tpl=self.passwordRecoveryTemplate)
user = self.userModule.viewSkel().all().filter("name.idx =", skel["name"].lower()).get()
skel_name = skel["name"]
if isinstance(skel_name, list): # Multiple emails: only use main one (first in list)
skel_name = skel_name[0]
user = self.userModule.viewSkel().all().filter("name.idx =", skel_name.lower()).get()

if not user or user["status"]<10: # Unknown user or locked account
skel.errors["name"] = _("Unknown user")
Expand Down Expand Up @@ -366,6 +371,14 @@ def canHandle(self, userKey):
user = db.Get(userKey)
return all([(x in user and (x=="otptimedrift" or bool(user[x]))) for x in ["otpid", "otpkey", "otptimedrift"]])

class otpSkel( RelSkel ):
otptoken = stringBone(descr="Token", required=True, caseSensitive=False, indexed=True)

def render(self, **params):
return self.userModule.render.edit(self.otpSkel(), action="otp",
tpl=self.userModule.loginSecondFactorTemplate,
params=params)

def startProcessing(self, userKey):
user = db.Get(userKey)
if all([(x in user and user[x]) for x in ["otpid", "otpkey"]]):
Expand All @@ -377,13 +390,10 @@ def startProcessing(self, userKey):
"timestamp": time(),
"failures": 0}
session.current.markChanged()
return self.userModule.render.loginSucceeded(msg="X-VIUR-2FACTOR-TimeBasedOTP")
return self.render()

return None

class otpSkel( RelSkel ):
otptoken = stringBone(descr="Token", required=True, caseSensitive=False, indexed=True)

def generateOtps(self, secret, timeDrift):
"""
Generates all valid tokens for the given secret
Expand Down Expand Up @@ -414,12 +424,13 @@ def asBytes(valIn):

@exposed
@forceSSL
def otp(self, otptoken = None, skey = None, *args, **kwargs ):
def otp(self, otptoken = None, skey = None, *args, **kwargs):
token = session.current.get("_otp_user")
if not token:
raise errors.Forbidden()
if otptoken is None:
self.userModule.render.edit(self.otpSkel())
return self.render()

if not securitykey.validate(skey):
raise errors.PreconditionFailed()
if token["failures"] > 3:
Expand All @@ -431,7 +442,12 @@ def otp(self, otptoken = None, skey = None, *args, **kwargs ):
otptoken = int(otptoken)
except:
# We got a non-numeric token - this cant be correct
self.userModule.render.edit(self.otpSkel(), tpl=self.otpTemplate)

return self.render()

#logging.debug(otptoken)
#logging.debug(validTokens)
#logging.debug(otptoken in validTokens)

if otptoken in validTokens:
userKey = session.current["_otp_user"]["uid"]
Expand All @@ -451,7 +467,8 @@ def otp(self, otptoken = None, skey = None, *args, **kwargs ):
token["failures"] += 1
session.current["_otp_user"] = token
session.current.markChanged()
return self.userModule.render.edit(self.otpSkel(), loginFailed=True, tpl=self.otpTemplate)

return self.render(secondFactorFailed=True)

def updateTimeDrift(self, userKey, idx):
"""
Expand All @@ -477,6 +494,7 @@ class User(List):
lostPasswordTemplate = "user_lostpassword"
verifyEmailAddressMail = "user_verify_address"
passwordRecoveryMail = "user_password_recovery"
loginSecondFactorTemplate = "user_login_secondfactor"

authenticationProviders = [UserPassword, GoogleAccount]
secondFactorProviders = [TimeBasedOTP]
Expand Down Expand Up @@ -729,6 +747,7 @@ def createNewUserIfNotExists():
"""
Create a new Admin user, if the userDB is empty
"""
logging.warning("createNewUserIfNotExists")
userMod = getattr(conf["viur.mainApp"], "user", None)
if (userMod # We have a user module
and isinstance(userMod, User)
Expand Down
18 changes: 9 additions & 9 deletions render/html/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,30 @@
import default
from server.skeleton import Skeleton

class Render( default.Render ): #Render user-data to xml
class Render(default.Render): #Render user-data to HTML
loginTemplate = "user_login"
logoutSuccessTemplate = "user_logout_success"
loginSuccessTemplate = "user_login_success"
logoutSuccessTemplate = "user_logout_success"
verifySuccessTemplate = "user_verify_success"
verifyFailedTemplate = "user_verify_failed"
passwdRecoverInfoTemplate = "user_passwdrecover_info"

def login(self, authMethods, tpl=None, **kwargs):
def login(self, skel, tpl=None, **kwargs):
if "loginTemplate" in dir(self.parent):
tpl = tpl or self.parent.loginTemplate
else:
tpl = tpl or self.loginTemplate

template = self.getEnv().get_template(self.getTemplateFileName(tpl))
return template.render(authMethods=authMethods, **kwargs)
return self.edit(skel, tpl=tpl, **kwargs)

def loginSucceeded( self, tpl=None, **kwargs ):
if "loginSuccessTemplate" in dir( self.parent ):
def loginSucceeded(self, tpl=None, **kwargs):
if "loginSuccessTemplate" in dir(self.parent):
tpl = tpl or self.parent.loginSuccessTemplate
else:
tpl = tpl or self.loginSuccessTemplate
template= self.getEnv().get_template( self.getTemplateFileName( tpl ) )
return( template.render( **kwargs ) )

template = self.getEnv().get_template(self.getTemplateFileName(tpl))
return template.render(**kwargs)

def logoutSuccess(self, tpl=None, **kwargs ):
if "logoutSuccessTemplate" in dir( self.parent ):
Expand Down
4 changes: 2 additions & 2 deletions render/json/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ def login(self, skel, **kwargs):

return self.edit(skel, **kwargs)

def loginSucceeded(self, msg = "OKAY", **kwargs):
return json.dumps(msg)
def loginSucceeded(self, **kwargs):
return json.dumps("OKAY")

def logoutSuccess(self, **kwargs):
return json.dumps("OKAY")
Expand Down
Loading