Source code for linotp.lib.user

# -*- coding: utf-8 -*-
#
#    LinOTP - the open source solution for two factor authentication
#    Copyright (C) 2010 - 2014 LSE Leading Security Experts GmbH
#
#    This file is part of LinOTP server.
#
#    This program is free software: you can redistribute it and/or
#    modify it under the terms of the GNU Affero General Public
#    License, version 3, as published by the Free Software Foundation.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU Affero General Public License for more details.
#
#    You should have received a copy of the
#               GNU Affero General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
#
#    E-mail: linotp@lsexperts.de
#    Contact: www.linotp.org
#    Support: www.lsexperts.de
#
""" contains user - related functions """
import logging
import re
import traceback
import sys

from linotp.lib.error   import UserError
from linotp.lib.util    import getParam
from linotp.lib.config  import getFromConfig, storeConfig
from linotp.lib.config  import getLinotpConfig
from linotp.lib.realm   import setDefaultRealm
from linotp.lib.realm   import getDefaultRealm
from linotp.lib.realm   import getRealms

from linotp.lib.resolver import splitResolver
from linotp.lib.resolver import getResolverObject


from linotp.lib.realm import createDBRealm
from linotp.lib.selftest import isSelfTest


ENCODING = 'utf-8'

log = logging.getLogger(__name__)


[docs]class User(object): def __init__(self, login="", realm="", conf=""): log.debug("[User.__init__] creating user %r,%r,%r" % (login, realm, conf)) self.login = "" self.realm = "" self.conf = "" if login is not None: self.login = login if realm is not None: self.realm = realm if conf is not None: self.conf = conf log.debug("[User.__init__] user created ") self.resolverUid = {} self.resolverConf = {} self.resolvers_list = []
[docs] def getRealm(self): return self.realm
[docs] def getResConf(self): return self.conf
[docs] def getUser(self): return self.login
[docs] def isEmpty(self): ## ignore if only conf is set! as it makes no sense if len(self.login) + len(self.realm) == 0: return True else: return False ##def __eq__(self,other): ## ret = False ## if other is None: ## if self.isEmpty() == True: ## return True ## else: ## return False ## if other.login == self.login and self.realm == other.realm and other.conf == self.conf: ## return True ## else: ## return False ##def __ne__(self,other): ## return not(self.__eq__(other))
def __str__(self): ret = str(None) if self.isEmpty() == False: loginname = "" try: loginname = unicode(self.login) except UnicodeEncodeError: loginname = unicode(self.login.encode(ENCODING)) conf = '' if self.conf is not None and len(self.conf) > 0: conf = '.%s' % (unicode(self.conf)) ret = '<%s%s@%s>' % (loginname, conf, unicode(self.realm)) return ret def __repr__(self): ret = ('User(login=%r, realm=%r, conf=%r ::resolverUid:%r, ' 'resolverConf:%r)' % (self.login, self.realm, self.conf, self.resolverUid, self.resolverConf)) return ret
[docs] def saveResolvers(self, resolvers): """ save the resolver objects as list as part of the user """ self.resolvers_list = resolvers
[docs] def getResolvers(self): return self.resolverUid.keys()
[docs] def addResolverUId(self, resolver, uid, conf="", resId="", resCId=""): self.resolverUid[resolver] = uid self.resolverConf[resolver] = (resId, resCId, conf)
[docs] def getResolverUId(self, resolver): uid = "" if self.resolverUid.has_key(resolver): uid = self.resolverUid.get(resolver) return uid
[docs] def getResolverConf(self, resolver): conf = "" if self.resolverConf.has_key(resolver): conf = self.resolverConf.get(resolver) return conf
[docs]def getUserResolverId(user, report=False): ## here we call the userid resolver!!" log.debug('getUserResolverId for %r' % user) (uuserid, uidResolver, uidResolverClass) = (u'', u'', u'') if (user is not None and user.isEmpty() != True): try: (uuserid, uidResolver, uidResolverClass) = getUserId(user) except Exception as e: log.error('[getUserResolverId] for %r@%r failed: %r' % (user.login, user.realm, e)) log.error("[getUserResolverId] %s" % traceback.format_exc()) if report == True: raise UserError("getUserResolverId failed: %r" % e, id=1112) return (uuserid, uidResolver, uidResolverClass)
[docs]def splitUser(username): user = username.strip() group = "" ## todo split the last l = user.split('@') if len(l) >= 2: (user, group) = user.rsplit('@') else: l = user.split('\\') if len(l) >= 2: (group, user) = user.rsplit('\\') return (user, group)
[docs]def getUserFromParam(param, optionalOrRequired): realm = "" conf = "" log.debug("[getUserFromParam] entering function") user = getParam(param, "user", optionalOrRequired) log.debug("[getUserFromParam] got user <<%r>>" % user) if user is None: user = "" else: splitAtSign = getFromConfig("splitAtSign", "true") if splitAtSign.lower() == "true": (user, realm) = splitUser(user) if param.has_key("realm"): realm = param["realm"] if user != "": if realm is None or realm == "" : realm = getDefaultRealm() usr = User(user, realm, "") if param.has_key("resConf"): conf = param["resConf"] ## with the short resolvernames, we have to extract the ## configuration name from the resolver spec if "(" in conf and ")" in conf: res_conf, resolver_typ = conf.split(" ") conf = res_conf usr.conf = conf else: if len(usr.login) > 0 or len(usr.realm) > 0 or len(usr.conf) > 0: res = getResolversOfUser(usr) usr.saveResolvers(res) if len(res) > 1: log.error("[getUserFromParam] user %r@%r in more than one " "resolver: %r" % (user, realm, res)) raise Exception("The user %s@%s is in more than one resolver:" " %s" % (user, realm, unicode(res))) log.debug("[getUserFromParam] creating user object %r,%r,%r" % (user, realm, conf)) log.debug("[getUserFromParam] created user object %r " % usr) return usr
[docs]def getUserFromRequest(request): ''' This function first tries to get the user from * a DigestAuth and otherwise from * basic auth and otherwise from * the client certificate ''' d_auth = { 'login' : '' } param = request.params try: # Do BasicAuth if request.environ.has_key('REMOTE_USER'): d_auth['login'] = request.environ['REMOTE_USER'] log.debug("[getUserFromRequest] BasicAuth: found the " "REMOTE_USER: %r" % d_auth) # Do DigestAuth elif request.environ.has_key('HTTP_AUTHORIZATION'): a_auth = request.environ['HTTP_AUTHORIZATION'].split(",") for field in a_auth: (key, _delimiter, value) = field.partition("=") d_auth[key.lstrip(' ')] = value.strip('"') if d_auth.has_key('Digest username'): d_auth['login'] = d_auth['Digest username'] log.debug("[getUserFromRequest] DigestAuth: found " "this HTTP_AUTHORIZATION: %r" % d_auth) # Do SSL Client Cert elif request.environ.has_key('SSL_CLIENT_S_DN_CN'): d_auth['login'] = request.environ.get('SSL_CLIENT_S_DN_CN') log.debug("[getUserFromRequest] SSLClientCert Auth: found " "this SSL_CLIENT_S_DN_CN: %r" % d_auth) # In case of selftest log.debug("[getUserFromRequest] Doing selftest!: %r" % isSelfTest()) if isSelfTest(): log.debug("[getUserFromRequest] Doing selftest!") param = request.params d_auth['login'] = getParam(param, "selftest_admin", True) log.debug("[getUserFromRequest] Found the user: %r in the request." % d_auth) except Exception as e: log.error("[getUserFromRequest] An error occurred when trying to fetch " "the user from the request: %r" % e) pass return d_auth
[docs]def setRealm(realm, resolvers): realm = realm.lower().strip() realm = realm.replace(" ", "-") nameExp = "^[A-Za-z0-9_\-\.]*$" res = re.match(nameExp, realm) if res is None: e = Exception("non conformant characters in realm name:" " %s (not in %s)" % (realm, nameExp)) raise e ret = storeConfig("useridresolver.group.%s" % realm, resolvers) if ret == False: return ret createDBRealm(realm) ## if this is the first one, make it the default realms = getRealms() if 1 == len(realms): for name in realms: setDefaultRealm(name) return True
[docs]def getUserRealms(user): ''' Returns the realms, a user belongs to. If the user has no realm but only a useridresolver, than all realms, containing this resolver are returned. This function is used for the policy module ''' allRealms = getRealms() Realms = [] if user.realm == "" and user.conf == "": defRealm = getDefaultRealm().lower() Realms.append(defRealm) user.realm = defRealm elif user.realm != "": Realms.append(user.realm.lower()) else: # we got a resolver and will get all realms the resolver belongs to. for key, v in allRealms.items(): log.debug("[getUserRealms] evaluating realm %r: %r " % (key, v)) for reso in v['useridresolver']: resotype, resoname = reso.rsplit('.', 1) log.debug("[getUserRealms] found resolver %r of type %r" % (resoname, resotype)) if resoname == user.conf: Realms.append(key.lower()) log.debug("[getUserRealms] added realm %r to Realms due to resolver %r" % (key, user.conf)) return Realms
[docs]def getRealmBox(): ''' returns the config value of selfservice.realmbox. if True, the realmbox in the selfservice login will be displayed. if False, the realmbox will not be displayed and the user needs to login via user@realm ''' rb_string = "linotp.selfservice.realmbox" log.debug("[getRealmBox] getting realmbox setting") conf = getLinotpConfig() if rb_string in conf: log.debug("[getRealmBox] read setting: %r" % conf[rb_string]) return "True" == conf[rb_string] else: return False
[docs]def getConf(Realms, Conf): """ extract the configguration part from the resolver definition """ for k in Realms: r = Realms[k] resIds = r["useridresolver"] for reso in resIds: (_package, _module, _class_, conf) = splitResolver(reso) if conf.lower() == Conf.lower(): return reso return ""
[docs]def getResolvers(user): ''' get the list of the Resolvers within a users.realm or from the resolver conf, if given in the user object :note: It ignores the user.login attribute! :param user: User with realm or resolver conf :type user: User object ''' Resolver = [] realms = getRealms(); if user.conf != "": reso = getConf(realms, user.conf) if len(reso) > 0: Resolver.append(reso) else: if user.realm != "": if user.realm.lower() in realms: Resolver = realms[user.realm.lower()]["useridresolver"] else: resDict = {} if user.realm.endswith('*') and len(user.realm) > 1: pattern = user.realm[:-1] for r in realms: if r.startswith(pattern): for idres in realms[r]["useridresolver"]: resDict[idres] = idres for k in resDict: Resolver.append(k) elif user.realm.endswith('*') and len(user.realm) == 1: for r in realms: for idres in realms[r]["useridresolver"]: resDict[idres] = idres for k in resDict: Resolver.append(k) else: for k in realms: r = realms[k] if r.has_key("default"): Resolver = r["useridresolver"] return Resolver
[docs]def getResolversOfUser(user): ''' This returns the list of the Resolvers of a user in a given realm. Usually this should only return one resolver input: user.login, user.realm returns: array of resolvers, the user was found in ''' login = user.login realm = user.realm Resolvers = user.getResolvers() if len(Resolvers) > 0: return Resolvers if realm is None or realm == "": realm = getDefaultRealm() #if realm is None or realm=="" or login is None or login == "": # log.error("[getResolversOfUser] You need to specify the name ( %s) and the realm (%s) of a user with conf %s" % (login, realm, user.conf)) realms = getRealms(); if user.conf != "": reso = getConf(realms, user.conf) if len(reso) > 0: Resolvers.append(reso) else: Realm_resolvers = getResolvers(User("", realm, "")) log.debug("[getResolversOfUser] check if user %r is in resolver %r" % (login, Realm_resolvers)) # Search for user in each resolver in the realm_ for realm_resolver in Realm_resolvers: log.debug("[getResolversOfUser] checking in %r" % realm_resolver) (package, module, class_, conf) = splitResolver(realm_resolver) module = package + "." + module y = getResolverObject(realm_resolver) if y is None: log.error("[getResolversOfUser] [ module %r not found!]" % (module)) try: log.debug("[getResolversOfUser] checking in module %r" % y) uid = y.getUserId(login) log.debug("[getResolversOfUser] type of uid: %s" % type(uid)) log.debug("[getResolversOfUser] type of realm_resolver: %s" % type(realm_resolver)) log.debug("[getResolversOfUser] type of login: %s" % type(login)) if uid not in ["", None]: log.info("[getResolversOfUser] user %r found in resolver %r" % (login, realm_resolver)) log.info("[getResolversOfUser] userid resolved to %r " % uid) ## Unicode Madness: ## This will break as soon as the unicode "uid" is put into a tuple ## v = (login, realm_resolver, uid) ## log.info("[getResolversOfUser] %s %s %s" % v) resId = y.getResolverId(); resCId = realm_resolver Resolvers.append(realm_resolver) user.addResolverUId(realm_resolver, uid, conf, resId, resCId) else: log.debug("[getResolversOfUser] user %r not found" " in resolver %r" % (login, realm_resolver)) except Exception as e: log.error("[getResolversOfUser] error searching user in" " module %r:%r" % (module, e)) log.error("[getResolversOfUser] %s" % traceback.format_exc()) log.debug("[getResolversOfUser] Resolvers: %r" % Resolvers) log.debug("[getResolversOfUser] Found the user %r in %r" % (login, Resolvers)) return Resolvers
[docs]def getUserId(user): """ getUserId (userObject) return (uid,resId,resIdC) """ uid = '' loginUser = u'' loginUser = user.login; resolvers = getResolversOfUser(user) for reso in resolvers: resId = "" resIdC = "" conf = "" uid = user.getResolverUId(reso) if uid != '': (resId, resIdC, conf) = user.getResolverConf(reso) break (package, module, class_, conf) = splitResolver(reso) if len(user.conf) > 0: if conf.lower() != user.conf.lower(): continue ## try to load the UserIdResolver Class try: module = package + "." + module log.debug("[getUserId] Getting resolver class: [%r] [%r]" % (module, class_)) y = getResolverObject(reso) log.debug("[getUserId] Getting UserID for user %r" % loginUser) uid = y.getUserId(loginUser) log.debug("[getUserId] Got UserId for user %r: %r" % (loginUser, uid)) log.debug("[getUserId] Retrieving ResolverID...") resId = y.getResolverId() resIdC = reso log.debug("[getUserId] Got ResolverID: %r, Loginuser: %r, " "Uid: %r ]" % (resId, loginUser, uid)) if uid != "": break; except Exception as e: log.error("[getUserId] module %r: %r ]" % (module, e)) continue if (uid == ''): log.warning("[getUserId] No uid found for the user >%r< in realm %r" % (loginUser, user.realm)) raise UserError(u"getUserId failed: no user >%s< found!" % unicode(loginUser), id=1205) log.debug("[getUserId] we are done!") return (unicode(uid), unicode(resId), unicode(resIdC))
[docs]def getSearchFields(User): searchFields = {} log.debug("[getSearchFields] entering function getSearchFields") for reso in getResolvers(User): """ """ (_package, module, class_, conf) = splitResolver(reso) if len(User.conf) > 0: if conf.lower() != User.conf.lower(): continue ## try to load the UserIdResolver Class try: y = getResolverObject(reso) sf = y.getSearchFields() searchFields[reso] = sf except Exception as e: log.warning("[getSearchField][ module %r: %r ]" % (module, e)) continue return searchFields
[docs]def getUserList(param, User): users = [] searchDict = {} log.debug("[getUserList] entering function getUserList") ## we have to recreate a new searchdict without the realm key ## as delete does not work for key in param: lval = param[key] if key == "realm": continue if key == "resConf": continue searchDict[key] = lval log.debug("[getUserList] Parameter key:%r=%r" % (key, lval)) resolverrrs = getResolvers(User) for reso in resolverrrs: (package, module, class_, conf) = splitResolver(reso) module = package + "." + module if len(User.conf) > 0: if conf.lower() != User.conf.lower(): continue ## try to load the UserIdResolver Class try: log.debug("[getUserList] Check for resolver class: %r" % reso) y = getResolverObject(reso) log.debug("[getUserList] with this search dictionary: %r " % searchDict) ulist = y.getUserList(searchDict) log.debug("[getUserList] setting the resolver <%r> for each user" % reso) for u in ulist: u["useridresolver"] = reso log.debug("[getUserList] Found this userlist: %r" % ulist) users.extend (ulist) except KeyError as exx: log.error("[getUserList][ module %r:%r ]" % (module, exx)) log.error("[getUserList] %s" % traceback.format_exc()) raise exx except Exception as exx: log.error("[getUserList][ module %r:%r ]" % (module, exx)) log.error("[getUserList] %s" % traceback.format_exc()) continue return users
[docs]def getUserInfo(userid, resolver, resolverC): log.debug("[getUserInfo] uid:%r resolver:%r class:%r" % (userid, resolver, resolverC)) ## [PasswdIdResolver] [IdResolver] userInfo = {} module = "" if not(userid): return userInfo try: (package, module, class_, conf) = splitResolver(resolverC) module = package + "." + module y = getResolverObject(resolverC) log.debug("[getUserInfo] Getting user info for userid " ">%r< in resolver" % userid) userInfo = y.getUserInfo(userid) except Exception as e: log.error("[getUserInfo][ module %r notfound! :%r ]" % (module, e)) return userInfo
[docs]def getUserPhone(user, phone_type='phone'): ''' Returns the phone numer of a user :param user: the user with the phone :type user: user object :param phone_type: The type of the phone, i.e. either mobile or phone (land line) :type phone_type: string :returns: list with phone numbers of this user object ''' (uid, resId, resClass) = getUserId(user) log.debug("[getUserPhone] got uid %r, ResId %r, Class %r" % (uid, resId, resClass)) userinfo = getUserInfo(uid, resId, resClass) if userinfo.has_key(phone_type): log.debug("[getUserPhone] got user phone %r of type %r" % (userinfo[phone_type], phone_type)) return userinfo[phone_type] else: log.warning("[getUserPhone] userobject (%r,%r,%r) has no phone of " "type %r." % (uid, resId, resClass, phone_type)) return ""
[docs]def check_user_password(username, realm, password): ''' This is a helper function to check the username and password against a userstore. return success --- This is the username of the authenticated user. If unsuccessful, returns None ''' success = None try: log.info("[check_user_password] User %r from realm %r tries to " "authenticate to selfservice" % (username, realm)) if type(username) != unicode: username = username.decode(ENCODING) u = User(username, realm, "") res = getResolversOfUser(u) # Now we know, the resolvers of this user and we can verify the password if (len(res) == 1): (uid, resolver, resolverC) = getUserId(u) log.info("[check_user_password] the user resolves to %r" % uid) log.info("[check_user_password] The username is found within the " "resolver %r" % resolver) # Authenticate user try: (package, module, class_, conf) = splitResolver(resolverC) module = package + "." + module y = getResolverObject(resolverC) except Exception as e: log.error("[check_user_password] [ module %r notfound! :%r ]" % (module, e)) try: if y.checkPass(uid, password): log.debug("[check_user_password] Successfully " "authenticated user %r." % username) # try: #identity = self.add_metadata( environ, identity ) success = username + '@' + realm else: log.info("[check_user_password] user %r failed " "to authenticate." % username) except Exception as e: log.error("[check_user_password] Error checking password " "within module %r:%r" % (module, e)) log.error("[check_user_password] %s" % traceback.format_exc()) elif (len(res) == 0): log.error("[check_user_password] The username %r exists in NO " "resolver within the realm %r." % (username, realm)) else: log.error("[check_user_password] The username %r exists in more " "than one resolver within the realm %r" % (username, realm)) log.error(res) except UserError as e: log.error("[check_user_password] Error while trying to verify " "the username: %r" % e.description) return success #eof###########################################################################