def test_extract_username(self): self.assertEquals(extract_username('username'), 'username') self.assertEquals(extract_username('*****@*****.**'), 'u2wqblarhim5su7pxemcbwdyryrghmuk') # test unicode/punycode (straight UTF8 and urlencoded) self.assertEquals(extract_username('*****@*****.**'), 'ym3nccfhvptfrhn7nkhhyvzgf2yl7r5y') # proper char self.assertRaises(UnicodeError, extract_username, '*****@*****.**') # bad utf-8 char self.assertRaises(UnicodeError, extract_username, '*****@*****.**') # invalid BIDI
def get_user_info(self, request, **params): """ Display the appropriate user page or discovery page """ user_info = {} user = str(request.sync_info['user']) params = {'user': user, 'host': self.app.config['oid.host'], 'config': self.app.config, 'request': request } uid = self.get_session_uid(request) if uid is not None: # Convert the user name to a standardized token user_name = extract_username(user) user_id = self.app.auth.backend.get_user_id(user_name) if user_id == uid: # hey that's me ! user_info = self.app.storage.get_user_info(user_id) or {} params['user_info'] = user_info params['sig'] = self.gen_signature(uid, request) # Use the older style of discovery (with link refs) template = get_template('user') ct = 'text/html' res = template.render(**params) response = Response(str(res), content_type=ct) if not user_info: response.delete_cookie('beaker.session.id') return response
def do_delete_account(self, request): """Do the delete.""" user_name = request.POST.get('username') password = request.POST.get('password') if user_name is None or password is None: return text_response('Missing data') user_name = extract_username(user_name) user_id = self.auth.authenticate_user(user_name, password) if user_id is None: return text_response('Bad credentials') # data deletion self.app.get_storage(request).delete_user(user_id) # user deletion (ldap etc.) user_id = self.auth.get_user_id(user_name) if user_id is not None: res = self.auth.delete_user(user_id, password) else: res = True if res: return text_response('Account removed.') else: return text_response('Deletion failed.')
def do_delete_account(self, request): """Do the delete.""" user_name = request.POST.get("username") password = request.POST.get("password") if user_name is None or password is None: return text_response("Missing data") user_name = extract_username(user_name) user_id = self.auth.authenticate_user(user_name, password) if user_id is None: return text_response("Bad credentials") # data deletion self.app.get_storage(request).delete_user(user_id) # user deletion (ldap etc.) user_id = self.auth.get_user_id(user_name) if user_id is not None: res = self.auth.delete_user(user_id, password) else: res = True if res: return text_response("Account removed.") else: return text_response("Deletion failed.")
def do_delete_account(self, request): """Do the delete.""" user_name = request.POST.get('username') password = request.POST.get('password') if user_name is None or password is None: return text_response('Missing data') user_name = extract_username(user_name) user = User(user_name) user_id = self.auth.authenticate_user(user, password) if user_id is None: return text_response('Bad credentials') # data deletion self.app.get_storage(request).delete_user(user_id) # user deletion (ldap etc.) res = self.auth.delete_user(user, password) if res: return text_response('Account removed.') else: return text_response('Deletion failed.')
def authenticate_user(self, request, config, username=None): """Authenticates a user and returns his id. "request" is the request received. The function makes sure that the user name found in the headers is compatible with the username if provided. It returns the user id from the database, if the password is the right one. """ environ = request.environ if 'REMOTE_USER' in environ: # already authenticated return environ['REMOTE_USER'] auth = environ.get('HTTP_AUTHORIZATION') if auth is not None: # for now, only supporting basic authentication # let's decipher the base64 encoded value if not auth.startswith('Basic '): raise HTTPUnauthorized('Invalid token') auth = auth[len('Basic '):].strip() try: user_name, password = base64.decodestring(auth).split(':') except (binascii.Error, ValueError): raise HTTPUnauthorized('Invalid token') # let's reject the call if the url is not owned by the user if (username is not None and user_name != username): log_cef('Username Does Not Match URL', 7, environ, config) raise HTTPUnauthorized() # if this is an email, hash it. Save the original for logging and # debugging. remote_user_original = user_name try: user_name = extract_username(user_name) except UnicodeError: log_cef('Invalid characters specified in username ', 5, environ, config) raise HTTPBadRequest( 'Invalid characters specified in ' + 'username', {}, 'Username must be BIDI ' + 'compliant UTF-8') # let's try an authentication user_id = self.backend.authenticate_user(user_name, password) if user_id is None: err = 'Authentication Failed for Backend service ' + user_name if remote_user_original is not None and \ user_name != remote_user_original: err += ' (%s)' % (remote_user_original) log_cef(err, 5, environ, config) raise HTTPUnauthorized() # we're all clear ! setting up REMOTE_USER request.remote_user = environ['REMOTE_USER'] = user_name # we also want to keep the password in clear text to reuse it # and remove it from the environ request.user_password = password request._authorization = environ['HTTP_AUTHORIZATION'] del environ['HTTP_AUTHORIZATION'] return user_id
def login(self, request, extra = {}, **kw): """ Log a user into the ID server """ response = {} error = {} uid = None email = None storage = self.app.storage if not self.is_internal(request): raise HTTPForbidden() (content_type, template) = self.get_template_from_request(request, html_template = 'login') # User is not logged in or the association is not present. if (len(request.POST.get('id', '')) and len(request.POST.get('password', ''))): email = request.POST['id'] password = request.POST['password'] try: username = extract_username(email) except UnicodeError: # Log the invalid username for diagnostics () logger.warn('Invalid username specified: %s (%s) ' % (email, username)) raise HTTPBadRequest() # user normalization complete, check to see if we know this # person uid = self.app.auth.backend.authenticate_user(username, password) if uid is None: error = self.error_codes.get('LOGIN_ERROR') logger.debug('Login failed for %s ' % email) body = template.render(error = error, response = response, extra = extra, request = request, config = self.app.config) response = Response(str(body), content_type = content_type) logger.debug('Nuking session cookie') response.delete_cookie('beaker.session.uid') try: del request.environ['beaker.session']['uid'] except KeyError: pass return response logger.debug('setting uid to %s' % uid) request.environ['beaker.session']['uid'] = uid # if this is an email validation, skip to that. if 'validate' in request.params: return self.validate(request) # Attempt to get the uid. if not email: email = request.params.get('id', None) if uid is None: logger.debug('attempting to get uid') uid = self.get_uid(request, strict = False) if uid is None: logger.debug('no uid present') # Presume that this is the first time in. # Display the login page for HTML only body = template.render(error = error, response = response, config = self.app.config, extra = extra, request = request) response = Response(str(body), content_type = content_type) response.delete_cookie('beaker.session.uid') return response # Ok, got a UID, so let's get the user info user = storage.get_user_info(uid) if user is None: user = storage.create_user(uid, email) if email: if email not in user.get('emails', []): self.send_validate_email(uid, email) if (len(request.params.get('audience', ''))): return self.registered_emails(request) location = "%s/%s" % (self.app.config.get('oid.login_host', 'localhost'), quote(email)) else: del (request.environ['beaker.session']['uid']) location = "%s/%s/login" % (self.app.config.get('oid.login_host', 'localhost'), VERSION) logger.debug('Sending user to admin page %s' % location) raise HTTPFound(location = location)
def authenticate_user(self, request, config, username=None): """Authenticates a user and returns his id. "request" is the request received. The function makes sure that the user name found in the headers is compatible with the username if provided. It returns the user id from the database, if the password is the right one. """ environ = request.environ if 'REMOTE_USER' in environ: # already authenticated return environ['REMOTE_USER'] auth = environ.get('HTTP_AUTHORIZATION') if auth is not None: # for now, only supporting basic authentication # let's decipher the base64 encoded value if not auth.startswith('Basic '): raise HTTPUnauthorized('Invalid token') auth = auth[len('Basic '):].strip() try: user_name, password = base64.decodestring(auth).split(':') except (binascii.Error, ValueError): raise HTTPUnauthorized('Invalid token') # let's reject the call if the url is not owned by the user if (username is not None and user_name != username): log_cef('Username Does Not Match URL', 7, environ, config) raise HTTPUnauthorized() # if this is an email, hash it. Save the original for logging and # debugging. remote_user_original = user_name try: user_name = extract_username(user_name) except UnicodeError: log_cef('Invalid characters specified in username ', 5, environ, config) raise HTTPBadRequest('Invalid characters specified in ' + 'username', {}, 'Username must be BIDI ' + 'compliant UTF-8') # let's try an authentication user_id = self.backend.authenticate_user(user_name, password) if user_id is None: err = 'Authentication Failed for Backend service ' + user_name if remote_user_original is not None and \ user_name != remote_user_original: err += ' (%s)' % (remote_user_original) log_cef(err, 5, environ, config) raise HTTPUnauthorized() # we're all clear ! setting up REMOTE_USER request.remote_user = environ['REMOTE_USER'] = user_name # we also want to keep the password in clear text to reuse it # and remove it from the environ request.user_password = password request._authorization = environ['HTTP_AUTHORIZATION'] del environ['HTTP_AUTHORIZATION'] return user_id