def _determine_auth_user(api_key, bearer_token, session_authuser): """ Create an `AuthUser` object given the API key/bearer token (if any) and the value of the authuser session cookie. """ # Authenticate by bearer token if bearer_token is not None: api_key = bearer_token # Authenticate by API key if api_key is not None: au = AuthUser(dbuser=User.get_by_api_key(api_key), authenticating_api_key=api_key, is_external_auth=True) if au.is_anonymous: log.warning('API key ****%s is NOT valid', api_key[-4:]) raise webob.exc.HTTPForbidden(_('Invalid API key')) return au # Authenticate by session cookie # In ancient login sessions, 'authuser' may not be a dict. # In that case, the user will have to log in again. # v0.3 and earlier included an 'is_authenticated' key; if present, # this must be True. if isinstance(session_authuser, dict) and session_authuser.get('is_authenticated', True): try: return AuthUser.from_cookie(session_authuser) except UserCreationError as e: # container auth or other auth functions that create users on # the fly can throw UserCreationError to signal issues with # user creation. Explanation should be provided in the # exception object. from kallithea.lib import helpers as h h.flash(e, 'error', logf=log.error) # Authenticate by auth_container plugin (if enabled) if any( plugin.is_container_auth for plugin in auth_modules.get_auth_plugins() ): try: user_info = auth_modules.authenticate('', '', request.environ) except UserCreationError as e: from kallithea.lib import helpers as h h.flash(e, 'error', logf=log.error) else: if user_info is not None: username = user_info['username'] user = User.get_by_username(username, case_insensitive=True) return log_in_user(user, remember=False, is_external_auth=True) # User is anonymous return AuthUser()
def password_reset_confirmation(self): if request.GET and request.GET.get('key'): try: user = User.get_by_api_key(request.GET.get('key')) data = dict(email=user.email) UserModel().reset_password(data) h.flash(_('Your password reset was successful, ' 'new password has been sent to your email'), category='success') except Exception, e: log.error(e) return redirect(url('reset_password'))
def password_reset_confirmation(self): if request.GET and request.GET.get('key'): try: user = User.get_by_api_key(request.GET.get('key')) data = dict(email=user.email) UserModel().reset_password(data) h.flash(_('Your password reset was successful, ' 'new password has been sent to your email'), category='success') except Exception, e: log.error(e) return redirect(url('reset_password'))
def __call__(self, environ, context): try: ip_addr = _get_ip_addr(environ) self._basic_security_checks() api_key = request.GET.get('api_key') try: # Request.authorization may raise ValueError on invalid input type, params = request.authorization except (ValueError, TypeError): pass else: if type.lower() == 'bearer': api_key = params # bearer token is an api key too if api_key is None: authuser = self._determine_auth_user( session.get('authuser'), ip_addr=ip_addr, ) needs_csrf_check = request.method not in ['GET', 'HEAD'] else: dbuser = User.get_by_api_key(api_key) if dbuser is None: log.info( 'No db user found for authentication with API key ****%s from %s', api_key[-4:], ip_addr) authuser = AuthUser.make(dbuser=dbuser, is_external_auth=True, ip_addr=ip_addr) needs_csrf_check = False # API key provides CSRF protection if authuser is None: log.info('No valid user found') raise webob.exc.HTTPForbidden() # set globals for auth user request.authuser = authuser request.ip_addr = ip_addr request.needs_csrf_check = needs_csrf_check log.info( 'IP: %s User: %s Request: %s', request.ip_addr, request.authuser, get_path_info(environ), ) return super(BaseController, self).__call__(environ, context) except webob.exc.HTTPException as e: return e
def _determine_auth_user(api_key, session_authuser): """ Create an `AuthUser` object given the API key (if any) and the value of the authuser session cookie. """ # Authenticate by API key if api_key: # when using API_KEY we are sure user exists. return AuthUser(dbuser=User.get_by_api_key(api_key), is_external_auth=True) # Authenticate by session cookie # In ancient login sessions, 'authuser' may not be a dict. # In that case, the user will have to log in again. # v0.3 and earlier included an 'is_authenticated' key; if present, # this must be True. if isinstance(session_authuser, dict) and session_authuser.get('is_authenticated', True): try: return AuthUser.from_cookie(session_authuser) except UserCreationError as e: # container auth or other auth functions that create users on # the fly can throw UserCreationError to signal issues with # user creation. Explanation should be provided in the # exception object. from kallithea.lib import helpers as h h.flash(e, 'error', logf=log.error) # Authenticate by auth_container plugin (if enabled) if any( auth_modules.importplugin(name).is_container_auth for name in Setting.get_auth_plugins() ): try: user_info = auth_modules.authenticate('', '', request.environ) except UserCreationError as e: from kallithea.lib import helpers as h h.flash(e, 'error', logf=log.error) else: if user_info is not None: username = user_info['username'] user = User.get_by_username(username, case_insensitive=True) return log_in_user(user, remember=False, is_external_auth=True) # User is anonymous return AuthUser()
self._req_method = json_body['method'] self._request_params = json_body['args'] if not isinstance(self._request_params, dict): self._request_params = {} log.debug( 'method: %s, params: %s' % (self._req_method, self._request_params) ) except KeyError, e: return jsonrpc_error(retid=self._req_id, message='Incorrect JSON query missing %s' % e) # check if we can find this session using api_key try: u = User.get_by_api_key(self._req_api_key) if u is None: return jsonrpc_error(retid=self._req_id, message='Invalid API KEY') #check if we are allowed to use this IP auth_u = AuthUser(u.user_id, self._req_api_key, ip_addr=ip_addr) if not auth_u.ip_allowed: return jsonrpc_error(retid=self._req_id, message='request from IP:%s not allowed' % (ip_addr,)) else: log.info('Access for IP:%s allowed' % (ip_addr,)) except Exception, e: return jsonrpc_error(retid=self._req_id, message='Invalid API KEY')
def get_by_api_key(self, api_key, cache=False): return User.get_by_api_key(api_key, cache)
def _handle_request(self, environ, start_response): start = time.time() ip_addr = self.ip_addr = self._get_ip_addr(environ) self._req_id = None if 'CONTENT_LENGTH' not in environ: log.debug("No Content-Length") return jsonrpc_error(retid=self._req_id, message="No Content-Length in request") else: length = environ['CONTENT_LENGTH'] or 0 length = int(environ['CONTENT_LENGTH']) log.debug('Content-Length: %s', length) if length == 0: log.debug("Content-Length is 0") return jsonrpc_error(retid=self._req_id, message="Content-Length is 0") raw_body = environ['wsgi.input'].read(length) try: json_body = json.loads(raw_body) except ValueError as e: # catch JSON errors Here return jsonrpc_error(retid=self._req_id, message="JSON parse error ERR:%s RAW:%r" % (e, raw_body)) # check AUTH based on API key try: self._req_api_key = json_body['api_key'] self._req_id = json_body['id'] self._req_method = json_body['method'] self._request_params = json_body['args'] if not isinstance(self._request_params, dict): self._request_params = {} log.debug( 'method: %s, params: %s', self._req_method, self._request_params ) except KeyError as e: return jsonrpc_error(retid=self._req_id, message='Incorrect JSON query missing %s' % e) # check if we can find this session using api_key try: u = User.get_by_api_key(self._req_api_key) if u is None: return jsonrpc_error(retid=self._req_id, message='Invalid API key') auth_u = AuthUser(dbuser=u) if not AuthUser.check_ip_allowed(auth_u, ip_addr): return jsonrpc_error(retid=self._req_id, message='request from IP:%s not allowed' % (ip_addr,)) else: log.info('Access for IP:%s allowed', ip_addr) except Exception as e: return jsonrpc_error(retid=self._req_id, message='Invalid API key') self._error = None try: self._func = self._find_method() except AttributeError as e: return jsonrpc_error(retid=self._req_id, message=str(e)) # now that we have a method, add self._req_params to # self.kargs and dispatch control to WGIController argspec = inspect.getargspec(self._func) arglist = argspec[0][1:] defaults = map(type, argspec[3] or []) default_empty = types.NotImplementedType # kw arguments required by this method func_kwargs = dict(izip_longest(reversed(arglist), reversed(defaults), fillvalue=default_empty)) # this is little trick to inject logged in user for # perms decorators to work they expect the controller class to have # authuser attribute set self.authuser = auth_u # This attribute will need to be first param of a method that uses # api_key, which is translated to instance of user at that name USER_SESSION_ATTR = 'apiuser' if USER_SESSION_ATTR not in arglist: return jsonrpc_error( retid=self._req_id, message='This method [%s] does not support ' 'authentication (missing %s param)' % ( self._func.__name__, USER_SESSION_ATTR) ) # get our arglist and check if we provided them as args for arg, default in func_kwargs.iteritems(): if arg == USER_SESSION_ATTR: # USER_SESSION_ATTR is something translated from API key and # this is checked before so we don't need validate it continue # skip the required param check if it's default value is # NotImplementedType (default_empty) if default == default_empty and arg not in self._request_params: return jsonrpc_error( retid=self._req_id, message=( 'Missing non optional `%s` arg in JSON DATA' % arg ) ) self._rpc_args = {USER_SESSION_ATTR: u} self._rpc_args.update(self._request_params) self._rpc_args['action'] = self._req_method self._rpc_args['environ'] = environ self._rpc_args['start_response'] = start_response status = [] headers = [] exc_info = [] def change_content(new_status, new_headers, new_exc_info=None): status.append(new_status) headers.extend(new_headers) exc_info.append(new_exc_info) output = WSGIController.__call__(self, environ, change_content) output = list(output) headers.append(('Content-Length', str(len(output[0])))) replace_header(headers, 'Content-Type', 'application/json') start_response(status[0], headers, exc_info[0]) log.info('IP: %s Request to %s time: %.3fs' % ( self._get_ip_addr(environ), safe_unicode(_get_access_path(environ)), time.time() - start) ) return output
def _dispatch(self, state, remainder=None): """ Parse the request body as JSON, look up the method on the controller and if it exists, dispatch to it. """ # Since we are here we should respond as JSON response.content_type = 'application/json' environ = state.request.environ start = time.time() ip_addr = request.ip_addr = self._get_ip_addr(environ) self._req_id = None if 'CONTENT_LENGTH' not in environ: log.debug("No Content-Length") raise JSONRPCErrorResponse(retid=self._req_id, message="No Content-Length in request") else: length = environ['CONTENT_LENGTH'] or 0 length = int(environ['CONTENT_LENGTH']) log.debug('Content-Length: %s', length) if length == 0: raise JSONRPCErrorResponse(retid=self._req_id, message="Content-Length is 0") raw_body = environ['wsgi.input'].read(length) try: json_body = json.loads(raw_body) except ValueError as e: # catch JSON errors Here raise JSONRPCErrorResponse( retid=self._req_id, message="JSON parse error ERR:%s RAW:%r" % (e, raw_body)) # check AUTH based on API key try: self._req_api_key = json_body['api_key'] self._req_id = json_body['id'] self._req_method = json_body['method'] self._request_params = json_body['args'] if not isinstance(self._request_params, dict): self._request_params = {} log.debug('method: %s, params: %s', self._req_method, self._request_params) except KeyError as e: raise JSONRPCErrorResponse( retid=self._req_id, message='Incorrect JSON query missing %s' % e) # check if we can find this session using api_key try: u = User.get_by_api_key(self._req_api_key) if u is None: raise JSONRPCErrorResponse(retid=self._req_id, message='Invalid API key') auth_u = AuthUser(dbuser=u) if not AuthUser.check_ip_allowed(auth_u, ip_addr): raise JSONRPCErrorResponse( retid=self._req_id, message='request from IP:%s not allowed' % (ip_addr, )) else: log.info('Access for IP:%s allowed', ip_addr) except Exception as e: raise JSONRPCErrorResponse(retid=self._req_id, message='Invalid API key') self._error = None try: self._func = self._find_method() except AttributeError as e: raise JSONRPCErrorResponse(retid=self._req_id, message=str(e)) # now that we have a method, add self._req_params to # self.kargs and dispatch control to WGIController argspec = inspect.getargspec(self._func) arglist = argspec[0][1:] defaults = map(type, argspec[3] or []) default_empty = types.NotImplementedType # kw arguments required by this method func_kwargs = dict( itertools.izip_longest(reversed(arglist), reversed(defaults), fillvalue=default_empty)) # this is little trick to inject logged in user for # perms decorators to work they expect the controller class to have # authuser attribute set request.authuser = request.user = auth_u # This attribute will need to be first param of a method that uses # api_key, which is translated to instance of user at that name USER_SESSION_ATTR = 'apiuser' # get our arglist and check if we provided them as args for arg, default in func_kwargs.iteritems(): if arg == USER_SESSION_ATTR: # USER_SESSION_ATTR is something translated from API key and # this is checked before so we don't need validate it continue # skip the required param check if it's default value is # NotImplementedType (default_empty) if default == default_empty and arg not in self._request_params: raise JSONRPCErrorResponse( retid=self._req_id, message='Missing non optional `%s` arg in JSON DATA' % arg, ) extra = set(self._request_params).difference(func_kwargs) if extra: raise JSONRPCErrorResponse( retid=self._req_id, message='Unknown %s arg in JSON DATA' % ', '.join('`%s`' % arg for arg in extra), ) self._rpc_args = {} self._rpc_args.update(self._request_params) self._rpc_args['action'] = self._req_method self._rpc_args['environ'] = environ log.info( 'IP: %s Request to %s time: %.3fs' % (self._get_ip_addr(environ), safe_unicode( _get_access_path(environ)), time.time() - start)) state.set_action(self._rpc_call, []) state.set_params(self._rpc_args) return state
def _dispatch(self, state, remainder=None): """ Parse the request body as JSON, look up the method on the controller and if it exists, dispatch to it. """ # Since we are here we should respond as JSON response.content_type = 'application/json' environ = state.request.environ start = time.time() ip_addr = request.ip_addr = self._get_ip_addr(environ) self._req_id = None if 'CONTENT_LENGTH' not in environ: log.debug("No Content-Length") raise JSONRPCErrorResponse(retid=self._req_id, message="No Content-Length in request") else: length = environ['CONTENT_LENGTH'] or 0 length = int(environ['CONTENT_LENGTH']) log.debug('Content-Length: %s', length) if length == 0: raise JSONRPCErrorResponse(retid=self._req_id, message="Content-Length is 0") raw_body = environ['wsgi.input'].read(length) try: json_body = json.loads(raw_body) except ValueError as e: # catch JSON errors Here raise JSONRPCErrorResponse(retid=self._req_id, message="JSON parse error ERR:%s RAW:%r" % (e, raw_body)) # check AUTH based on API key try: self._req_api_key = json_body['api_key'] self._req_id = json_body['id'] self._req_method = json_body['method'] self._request_params = json_body['args'] if not isinstance(self._request_params, dict): self._request_params = {} log.debug('method: %s, params: %s', self._req_method, self._request_params) except KeyError as e: raise JSONRPCErrorResponse(retid=self._req_id, message='Incorrect JSON query missing %s' % e) # check if we can find this session using api_key try: u = User.get_by_api_key(self._req_api_key) if u is None: raise JSONRPCErrorResponse(retid=self._req_id, message='Invalid API key') auth_u = AuthUser(dbuser=u) if not AuthUser.check_ip_allowed(auth_u, ip_addr): raise JSONRPCErrorResponse(retid=self._req_id, message='request from IP:%s not allowed' % (ip_addr,)) else: log.info('Access for IP:%s allowed', ip_addr) except Exception as e: raise JSONRPCErrorResponse(retid=self._req_id, message='Invalid API key') self._error = None try: self._func = self._find_method() except AttributeError as e: raise JSONRPCErrorResponse(retid=self._req_id, message=str(e)) # now that we have a method, add self._req_params to # self.kargs and dispatch control to WGIController argspec = inspect.getargspec(self._func) arglist = argspec[0][1:] defaults = map(type, argspec[3] or []) default_empty = types.NotImplementedType # kw arguments required by this method func_kwargs = dict(itertools.izip_longest(reversed(arglist), reversed(defaults), fillvalue=default_empty)) # this is little trick to inject logged in user for # perms decorators to work they expect the controller class to have # authuser attribute set request.authuser = request.user = auth_u # This attribute will need to be first param of a method that uses # api_key, which is translated to instance of user at that name USER_SESSION_ATTR = 'apiuser' # get our arglist and check if we provided them as args for arg, default in func_kwargs.iteritems(): if arg == USER_SESSION_ATTR: # USER_SESSION_ATTR is something translated from API key and # this is checked before so we don't need validate it continue # skip the required param check if it's default value is # NotImplementedType (default_empty) if default == default_empty and arg not in self._request_params: raise JSONRPCErrorResponse( retid=self._req_id, message='Missing non optional `%s` arg in JSON DATA' % arg, ) extra = set(self._request_params).difference(func_kwargs) if extra: raise JSONRPCErrorResponse( retid=self._req_id, message='Unknown %s arg in JSON DATA' % ', '.join('`%s`' % arg for arg in extra), ) self._rpc_args = {} self._rpc_args.update(self._request_params) self._rpc_args['action'] = self._req_method self._rpc_args['environ'] = environ log.info('IP: %s Request to %s time: %.3fs' % ( self._get_ip_addr(environ), safe_unicode(_get_access_path(environ)), time.time() - start) ) state.set_action(self._rpc_call, []) state.set_params(self._rpc_args) return state
self._req_api_key = json_body['api_key'] self._req_id = json_body['id'] self._req_method = json_body['method'] self._request_params = json_body['args'] if not isinstance(self._request_params, dict): self._request_params = {} log.debug('method: %s, params: %s' % (self._req_method, self._request_params)) except KeyError, e: return jsonrpc_error(retid=self._req_id, message='Incorrect JSON query missing %s' % e) # check if we can find this session using api_key try: u = User.get_by_api_key(self._req_api_key) if u is None: return jsonrpc_error(retid=self._req_id, message='Invalid API KEY') #check if we are allowed to use this IP auth_u = AuthUser(u.user_id, self._req_api_key, ip_addr=ip_addr) if not auth_u.ip_allowed: return jsonrpc_error(retid=self._req_id, message='request from IP:%s not allowed' % (ip_addr, )) else: log.info('Access for IP:%s allowed' % (ip_addr, )) except Exception, e: return jsonrpc_error(retid=self._req_id, message='Invalid API KEY')