class BaseController(WSGIController): def __call__(self, environ, start_response): """Invoke the Controller""" # WSGIController.__call__ dispatches to the Controller method # the request is routed to. This routing information is # available in environ['pylons.routes_dict'] return WSGIController.__call__(self, environ, start_response) def __init__(self, *args, **kw): self.context = {} self.conn = None self.request = request self.response = response self.set_language(request.headers) self.parent = super(WSGIController, self) self.parent.__init__(*args, **kw) self.config = app_config['app_conf'] self.here = app_config['here'] self.browser_language = request.headers.get('Accept-Language', None) try: self.base_url = self.config['linotp_url'] #trim trailing slashes while self.base_url[-1] == '/': self.base_url = self.base_url[:-1] except KeyError: raise Exception("Missing definition of remote linotp url" " in application ini: linotp_url") # load keyfile client_key = self.config.get('client_key', None) self.client_key = None # replace the app root %here% if any if client_key and '%(here)s' in client_key: client_key = client_key.replace('%(here)s', self.here) if client_key: if os.path.exists(client_key): self.client_key = client_key else: log.error("key_file %s could not be found", client_key) # load the client certificate file client_cert = self.config.get('client_cert', None) self.client_cert = None # replace the app root %here% if any if client_cert and '%(here)s' in client_cert: client_cert = client_cert.replace('%(here)s', self.here) if client_cert: if os.path.exists(client_cert): self.client_cert = client_cert else: log.error("cert_file %s could not be found", client_cert) # load the server certificate file server_cert = self.config.get('server_cert', None) self.server_cert = None # replace the app root %here% if any if server_cert and '%(here)s' in server_cert: server_cert = server_cert.replace('%(here)s', self.here) if server_cert: if os.path.exists(server_cert): self.server_cert = server_cert else: log.error("cert_file %s could not be found", server_cert) self.remote_base = self.config.get('linotp_remote_base', '/userservice') return def call_linotp(self, url, params=None, return_json=True): """ make a http request to the linotp server :param url: the path of the linotp resource :param params: dict with request parameters :param return_json: bool, response should already be a json loaded obj :return: return the response of the request as dict or as plain text """ if not self.conn: self.conn = Connection( self.base_url, server_cert=self.server_cert, client_cert=self.client_cert, client_key=self.client_key ) if params is None: params = {} headers = {"Content-type": "application/x-www-form-urlencoded", "Accept": "text/plain", } # for locale support, we copy the incomming languege settings if self.browser_language: headers['Accept-Language'] = self.browser_language if not self.conn.is_user_session_set: # if we are requesting for a user, provide the user auth cookie if 'user' in params: if hasattr(self, 'auth_cookie') and self.auth_cookie: self.conn.set_user_session(self.auth_cookie, params['user']) path = url if 'session' in params: # If a session is contained in params it is the local selfservice # session (between the browser and this server) not the session # between selfservice and LinOTP. Therefore we delete it. The # selfservice->LinOTP session has already been set with # 'self.conn.set_user_session' del params['session'] net_response = self.conn.post(path, params=params, headers=headers) if net_response.status_code != 200: error = "%s%s: %s - %s" % (self.config.get('linotp_url', ''), path, net_response.status_code, net_response.reason) log.error(error) raise InvalidLinOTPResponse(error, url=self.config.get('linotp_url', ''), path=path, status_code=net_response.status_code, reason=net_response.reason) return net_response.json() if return_json else net_response.text() def get_preauth_context(self, params=None): """ get required context information before the user is authenticated """ if params is None: params = {} context = self.call_linotp('/userservice/pre_context', params=params) return context def get_context(self, params=None): """ retrieve the selfservice defintion in the scope of the authenticated user """ if params is None: params = {} context = self.call_linotp('/userservice/context', params=params) return context def set_language(self, headers): '''Invoke before everything else. And set the translation language''' languages = headers.get('Accept-Language', '').split(';') found_lang = False for language in languages: for lang in language.split(','): try: if lang[:2] == "en": found_lang = True break if lang == 'de': pass set_lang(lang) found_lang = True break except LanguageError as exx: pass if found_lang is True: break if found_lang is False: log.warning("Cannot set preferred language: %r" % languages) return def sendError(self, response, exception, errId=311, context=None): """ return an error response to the client """ version = get_version() id = '1.0' ## handle the different types of exception: ## Exception, LinOtpError, str/unicode if (hasattr(exception, '__class__') == True and isinstance(exception, Exception)): errDesc = unicode(exception) elif type(exception) in [str, unicode]: errDesc = unicode(exception) else: errDesc = u"%r" % exception response.content_type = 'application/json' res = { "jsonrpc": "2.0", "result" : {"status": False, "error": { "code" : errId, "message" : errDesc, }, }, "version": version, "id": id } ret = json.dumps(res, indent=3) if context in ['before', 'after']: response._exception = exception response.body = ret ret = response return res
class LinOTPUserAuthPlugin(object): implements(IAuthenticator) def __init__(self, linotp_url, client_cert=None, client_key=None, server_cert=None): self.conn = None self.base_url = linotp_url.strip('/') # load keyfile self.client_key = None # replace the app root %here% if any if client_key and '%(here)s' in client_key: client_key = client_key.replace('%(here)s', self.here) if client_key: if os.path.exists(client_key): self.client_key = client_key else: log.error("key_file %s could not be found", client_key) # load the client certificate file self.client_cert = None # replace the app root %here% if any if client_cert and '%(here)s' in client_cert: client_cert = client_cert.replace('%(here)s', self.here) if client_cert: if os.path.exists(client_cert): self.client_cert = client_cert else: log.error("cert_file %s could not be found", client_cert) # load the server certificate file self.server_cert = None # replace the app root %here% if any if server_cert and '%(here)s' in server_cert: server_cert = server_cert.replace('%(here)s', self.here) if server_cert: if os.path.exists(server_cert): self.server_cert = server_cert else: log.error("cert_file %s could not be found", server_cert) # IAuthenticatorPlugin def authenticate(self, environ, identity): """ authenticated user to the identity. If a auth cookie is given it is appended to the indentiy. :param environ: The WSGI environment. :param identity: The repoze.who's identity dictionary. """ if 'login' not in identity or 'password' not in identity: return None login = identity['login'] password = identity['password'] try: if not self.conn: self.conn = Connection( self.base_url, server_cert=self.server_cert, client_cert=self.client_cert, client_key=self.client_key ) params = {'login':login, 'password': password} headers = {"Content-type": "application/x-www-form-urlencoded", "Accept": "text/plain"} if environ.get('HTTP_ACCEPT_LANGUAGE', None): headers['Accept-Language'] = environ.get('HTTP_ACCEPT_LANGUAGE', None) path = "/userservice/auth" response = self.conn.post(path, params=params, headers=headers) if response.status_code != 200: return "::ERROR:: Connection response: %s (%s) %r" % ( response.reason, path, response.status_code ) res = response.json() authUser = res.get('result', {}).get('value', False) if authUser != False: cookie = response.get_cookie('userauthcookie') if cookie: return "%s;%s" % (login, cookie) else: return login else: return "::ERROR:: Authentication attempt failed!" except Exception as exx: log.error("[authenticate] %r" % exx) return "::ERROR:: Connection failed!" def __repr__(self): return '<%s %s>' % (self.__class__.__name__, id(self))
class LinOTPUserModelPlugin(LinOTPUserAuthPlugin): implements(IMetadataProvider) def __init__(self, linotp_url, client_cert=None, client_key=None, server_cert=None): self.parent = super(LinOTPUserModelPlugin, self) self.parent.__init__(linotp_url, client_cert, client_key, server_cert) # IMetadataProvider def add_metadata(self, environ, identity): """ Add metadata about the authenticated user to the identity. It modifies the C{identity} dictionary to add the metadata. :param environ: The WSGI environment. :param identity: The repoze.who's identity dictionary. """ params = {} headers = {"Content-type": "application/x-www-form-urlencoded", "Accept": "text/plain"} # this part is used implicit: # on logout, there is no login and thus just return a None path = environ.get('PATH_INFO', '/logout') if '/logout' in path: return None # due to the requirement to transfer info back from repoze # authentication, we have to deal with the ::ERROR:: user if (identity and "::ERROR::" in identity.get('repoze.who.user', '::ERROR::')): return None try: if not self.conn: self.conn = Connection( self.base_url, server_cert=self.server_cert, client_cert=self.client_cert, client_key=self.client_key ) if environ.get('HTTP_ACCEPT_LANGUAGE', None): headers['Accept-Language'] = environ.get('HTTP_ACCEPT_LANGUAGE', None) if not self.conn.is_user_session_set: # for the authetication we take the 'repoze.who.userid' as it # is the one which is returned from the authenticate call # extended by the realm and joined with the auth_cookie if ';' in identity['repoze.who.userid']: user, session = identity['repoze.who.userid'].split(';', 1) self.conn.set_user_session(session, user) else: user = identity['repoze.who.userid'] self.conn.set_user_session(None, user) path = "/userservice/userinfo" response = self.conn.post(path, params=params, headers=headers) if response.status_code == 200: res = response.json() user_data = res.get('result', {}).get('value', []) if type(user_data) in [dict]: identity.update(user_data) # implicit return of enriched identity return identity except Exception as exx: log.error("[add_metadata] %r" % exx) return "::ERROR:: Connection failed!" def __repr__(self): return '<%s %s>' % (self.__class__.__name__, id(self))
class LinOTPUserAuthPlugin(object): implements(IAuthenticator) def __init__(self, linotp_url, client_cert=None, client_key=None, server_cert=None): self.conn = None self.base_url = linotp_url.strip('/') # load keyfile self.client_key = None # replace the app root %here% if any if client_key and '%(here)s' in client_key: client_key = client_key.replace('%(here)s', self.here) if client_key: if os.path.exists(client_key): self.client_key = client_key else: log.error("key_file %s could not be found", client_key) # load the client certificate file self.client_cert = None # replace the app root %here% if any if client_cert and '%(here)s' in client_cert: client_cert = client_cert.replace('%(here)s', self.here) if client_cert: if os.path.exists(client_cert): self.client_cert = client_cert else: log.error("cert_file %s could not be found", client_cert) # load the server certificate file self.server_cert = None # replace the app root %here% if any if server_cert and '%(here)s' in server_cert: server_cert = server_cert.replace('%(here)s', self.here) if server_cert: if os.path.exists(server_cert): self.server_cert = server_cert else: log.error("cert_file %s could not be found", server_cert) # IAuthenticatorPlugin def authenticate(self, environ, identity): """ authenticated user to the identity. If a auth cookie is given it is appended to the indentiy. :param environ: The WSGI environment. :param identity: The repoze.who's identity dictionary. """ if 'login' not in identity or 'password' not in identity: return None login = identity['login'] password = identity['password'] try: if not self.conn: self.conn = Connection(self.base_url, server_cert=self.server_cert, client_cert=self.client_cert, client_key=self.client_key) params = {'login': login, 'password': password} headers = { "Content-type": "application/x-www-form-urlencoded", "Accept": "text/plain" } if environ.get('HTTP_ACCEPT_LANGUAGE', None): headers['Accept-Language'] = environ.get( 'HTTP_ACCEPT_LANGUAGE', None) path = "/userservice/auth" response = self.conn.post(path, params=params, headers=headers) if response.status_code != 200: return "::ERROR:: Connection response: %s (%s) %r" % ( response.reason, path, response.status_code) res = response.json() authUser = res.get('result', {}).get('value', False) if authUser != False: cookie = response.get_cookie('userauthcookie') if cookie: return "%s;%s" % (login, cookie) else: return login else: return "::ERROR:: Authentication attempt failed!" except Exception as exx: log.error("[authenticate] %r" % exx) return "::ERROR:: Connection failed!" def __repr__(self): return '<%s %s>' % (self.__class__.__name__, id(self))
class LinOTPUserModelPlugin(LinOTPUserAuthPlugin): implements(IMetadataProvider) def __init__(self, linotp_url, client_cert=None, client_key=None, server_cert=None): self.parent = super(LinOTPUserModelPlugin, self) self.parent.__init__(linotp_url, client_cert, client_key, server_cert) # IMetadataProvider def add_metadata(self, environ, identity): """ Add metadata about the authenticated user to the identity. It modifies the C{identity} dictionary to add the metadata. :param environ: The WSGI environment. :param identity: The repoze.who's identity dictionary. """ params = {} headers = { "Content-type": "application/x-www-form-urlencoded", "Accept": "text/plain" } # this part is used implicit: # on logout, there is no login and thus just return a None path = environ.get('PATH_INFO', '/logout') if '/logout' in path: return None # due to the requirement to transfer info back from repoze # authentication, we have to deal with the ::ERROR:: user if (identity and "::ERROR::" in identity.get('repoze.who.user', '::ERROR::')): return None try: if not self.conn: self.conn = Connection(self.base_url, server_cert=self.server_cert, client_cert=self.client_cert, client_key=self.client_key) if environ.get('HTTP_ACCEPT_LANGUAGE', None): headers['Accept-Language'] = environ.get( 'HTTP_ACCEPT_LANGUAGE', None) if not self.conn.is_user_session_set: # for the authetication we take the 'repoze.who.userid' as it # is the one which is returned from the authenticate call # extended by the realm and joined with the auth_cookie if ';' in identity['repoze.who.userid']: user, session = identity['repoze.who.userid'].split(';', 1) self.conn.set_user_session(session, user) else: user = identity['repoze.who.userid'] self.conn.set_user_session(None, user) path = "/userservice/userinfo" response = self.conn.post(path, params=params, headers=headers) if response.status_code == 200: res = response.json() user_data = res.get('result', {}).get('value', []) if type(user_data) in [dict]: identity.update(user_data) # implicit return of enriched identity return identity except Exception as exx: log.error("[add_metadata] %r" % exx) return "::ERROR:: Connection failed!" def __repr__(self): return '<%s %s>' % (self.__class__.__name__, id(self))
class BaseController(WSGIController): def __call__(self, environ, start_response): """Invoke the Controller""" # WSGIController.__call__ dispatches to the Controller method # the request is routed to. This routing information is # available in environ['pylons.routes_dict'] return WSGIController.__call__(self, environ, start_response) def __init__(self, *args, **kw): self.context = {} self.conn = None self.request = request self.response = response self.set_language(request.headers) self.parent = super(WSGIController, self) self.parent.__init__(*args, **kw) self.config = app_config['app_conf'] self.here = app_config['here'] self.browser_language = request.headers.get('Accept-Language', None) try: self.base_url = self.config['linotp_url'] #trim trailing slashes while self.base_url[-1] == '/': self.base_url = self.base_url[:-1] except KeyError: raise Exception("Missing definition of remote linotp url" " in application ini: linotp_url") # load keyfile client_key = self.config.get('client_key', None) self.client_key = None # replace the app root %here% if any if client_key and '%(here)s' in client_key: client_key = client_key.replace('%(here)s', self.here) if client_key: if os.path.exists(client_key): self.client_key = client_key else: log.error("key_file %s could not be found", client_key) # load the client certificate file client_cert = self.config.get('client_cert', None) self.client_cert = None # replace the app root %here% if any if client_cert and '%(here)s' in client_cert: client_cert = client_cert.replace('%(here)s', self.here) if client_cert: if os.path.exists(client_cert): self.client_cert = client_cert else: log.error("cert_file %s could not be found", client_cert) # load the server certificate file server_cert = self.config.get('server_cert', None) self.server_cert = None # replace the app root %here% if any if server_cert and '%(here)s' in server_cert: server_cert = server_cert.replace('%(here)s', self.here) if server_cert: if os.path.exists(server_cert): self.server_cert = server_cert else: log.error("cert_file %s could not be found", server_cert) self.remote_base = self.config.get('linotp_remote_base', '/userservice') return def call_linotp(self, url, params=None, return_json=True): """ make a http request to the linotp server :param url: the path of the linotp resource :param params: dict with request parameters :param return_json: bool, response should already be a json loaded obj :return: return the response of the request as dict or as plain text """ if not self.conn: self.conn = Connection(self.base_url, server_cert=self.server_cert, client_cert=self.client_cert, client_key=self.client_key) if params is None: params = {} headers = { "Content-type": "application/x-www-form-urlencoded", "Accept": "text/plain", } # for locale support, we copy the incomming languege settings if self.browser_language: headers['Accept-Language'] = self.browser_language if not self.conn.is_user_session_set: # if we are requesting for a user, provide the user auth cookie if 'user' in params: if hasattr(self, 'auth_cookie') and self.auth_cookie: self.conn.set_user_session(self.auth_cookie, params['user']) path = url if 'session' in params: # If a session is contained in params it is the local selfservice # session (between the browser and this server) not the session # between selfservice and LinOTP. Therefore we delete it. The # selfservice->LinOTP session has already been set with # 'self.conn.set_user_session' del params['session'] net_response = self.conn.post(path, params=params, headers=headers) if net_response.status_code != 200: error = "%s%s: %s - %s" % (self.config.get( 'linotp_url', ''), path, net_response.status_code, net_response.reason) log.error(error) if net_response.reason == "Logout from LinOTP selfservice": raise SessionExpiratioException( error, url=self.config.get('linotp_url', ''), path=path, status_code=net_response.status_code, reason=net_response.reason) raise InvalidLinOTPResponse(error, url=self.config.get('linotp_url', ''), path=path, status_code=net_response.status_code, reason=net_response.reason) return net_response.json() if return_json else net_response.text() def get_preauth_context(self, params=None): """ get required context information before the user is authenticated """ if params is None: params = {} context = self.call_linotp('/userservice/pre_context', params=params) return context def get_context(self, params=None): """ retrieve the selfservice defintion in the scope of the authenticated user """ if params is None: params = {} context = self.call_linotp('/userservice/context', params=params) return context def set_language(self, headers): '''Invoke before everything else. And set the translation language''' languages = headers.get('Accept-Language', '') found_lang = False for match in accept_language_regexp.finditer(languages): # make sure we have a correct language code format language = match.group(1) if not language: continue language = language.replace('_', '-').lower() # en is the default language if language.split('-')[0] == 'en': found_lang = True break try: set_lang(language.split('-')[0]) found_lang = True break except LanguageError: log.debug( "Cannot set requested language: %s. Trying next language if available.", language) if not found_lang: log.warning("Cannot set preferred language: %r" % languages) return def sendError(self, response, exception, errId=311, context=None): """ return an error response to the client """ version = get_version() id = '1.0' ## handle the different types of exception: ## Exception, LinOtpError, str/unicode if (hasattr(exception, '__class__') == True and isinstance(exception, Exception)): errDesc = unicode(exception) elif type(exception) in [str, unicode]: errDesc = unicode(exception) else: errDesc = u"%r" % exception response.content_type = 'application/json' res = { "jsonrpc": "2.0", "result": { "status": False, "error": { "code": errId, "message": errDesc, }, }, "version": version, "id": id } ret = json.dumps(res, indent=3) if context in ['before', 'after']: response._exception = exception response.body = ret ret = response return res