def login_whistleblower(session, tid, receipt): """ Login transaction for whistleblowers' access :param session: An ORM session :param tid: A tenant ID :param receipt: A provided receipt :return: Returns a user session in case of success """ x = None algorithms = [x[0] for x in session.query(WhistleblowerTip.hash_alg).filter(WhistleblowerTip.tid == tid).distinct()] if algorithms: hashes = [GCE.hash_password(receipt, State.tenant_cache[tid].receipt_salt, alg) for alg in algorithms] x = session.query(WhistleblowerTip, InternalTip) \ .filter(WhistleblowerTip.receipt_hash.in_(hashes), WhistleblowerTip.tid == tid, InternalTip.id == WhistleblowerTip.id).one_or_none() if x is None: login_failure(tid, 1) wbtip = x[0] itip = x[1] itip.wb_last_access = datetime_now() crypto_prv_key = '' if wbtip.crypto_prv_key: user_key = GCE.derive_key(receipt.encode(), State.tenant_cache[tid].receipt_salt) crypto_prv_key = GCE.symmetric_decrypt(user_key, Base64Encoder.decode(wbtip.crypto_prv_key)) return Sessions.new(tid, wbtip.id, tid, 'whistleblower', False, False, crypto_prv_key, '')
def login_whistleblower(session, tid, receipt, client_using_tor): """ login_whistleblower returns a session """ hashed_receipt = security.hash_password( receipt, State.tenant_cache[tid].receipt_salt) result = session.query(WhistleblowerTip, InternalTip) \ .filter(WhistleblowerTip.receipt_hash == text_type(hashed_receipt, 'utf-8'), WhistleblowerTip.tid == tid, InternalTip.id == WhistleblowerTip.id, InternalTip.tid == WhistleblowerTip.tid).first() if result is None: log.debug("Whistleblower login: Invalid receipt") Settings.failed_login_attempts += 1 raise errors.InvalidAuthentication wbtip, itip = result[0], result[1] if not client_using_tor and not State.tenant_cache[tid][ 'https_whistleblower']: log.err("Denied login request over clear Web for role 'whistleblower'") raise errors.TorNetworkRequired itip.wb_last_access = datetime_now() return Sessions.new(tid, wbtip.id, 'whistleblower', False)
def login_whistleblower(session, tid, receipt): """ login_whistleblower returns a session """ x = None algorithms = [x[0] for x in session.query(WhistleblowerTip.hash_alg).filter(WhistleblowerTip.tid == tid).distinct()] if algorithms: hashes = [] for alg in algorithms: hashes.append(GCE.hash_password(receipt, State.tenant_cache[tid].receipt_salt, alg)) x = session.query(WhistleblowerTip, InternalTip) \ .filter(WhistleblowerTip.receipt_hash.in_(hashes), WhistleblowerTip.tid == tid, InternalTip.id == WhistleblowerTip.id, InternalTip.tid == WhistleblowerTip.tid).one_or_none() if x is None: log.debug("Whistleblower login: Invalid receipt") Settings.failed_login_attempts += 1 raise errors.InvalidAuthentication wbtip = x[0] itip = x[1] itip.wb_last_access = datetime_now() crypto_prv_key = '' if State.tenant_cache[tid].encryption and wbtip.crypto_prv_key: user_key = GCE.derive_key(receipt.encode('utf-8'), State.tenant_cache[tid].receipt_salt) crypto_prv_key = GCE.symmetric_decrypt(user_key, wbtip.crypto_prv_key) return Sessions.new(tid, wbtip.id, tid, 'whistleblower', False, crypto_prv_key)
def login(session, tid, username, password, authcode, client_using_tor, client_ip): """ login returns a session """ user = None users = session.query(User).filter(User.username == username, User.state != 'disabled', User.tid == tid).distinct() for u in users: if GCE.check_password(u.hash_alg, password, u.salt, u.password): user = u break # Fix for issue: https://github.com/globaleaks/GlobaLeaks/issues/2563 if State.tenant_cache[1].creation_date < 1551740400: u_password = '******'' + u.password + '\'' if GCE.check_password(u.hash_alg, password, u.salt, u_password): user = u break if user is None: log.debug("Login: Invalid credentials") Settings.failed_login_attempts += 1 raise errors.InvalidAuthentication connection_check(client_ip, tid, user.role, client_using_tor) crypto_prv_key = '' if State.tenant_cache[tid].encryption: if user.crypto_prv_key: user_key = GCE.derive_key(password.encode('utf-8'), user.salt) crypto_prv_key = GCE.symmetric_decrypt(user_key, user.crypto_prv_key) else: # Force the password change on which the user key will be created user.password_change_needed = True if user.two_factor_enable: if authcode != '': if user.crypto_pub_key: two_factor_secret = GCE.asymmetric_decrypt( crypto_prv_key, user.two_factor_secret).decode('utf-8') else: two_factor_secret = user.two_factor_secret.decode('utf-8') # RFC 6238: step size 30 sec; valid_window = 1; total size of the window: 1.30 sec if not pyotp.TOTP(two_factor_secret).verify(authcode, valid_window=1): raise errors.InvalidTwoFactorAuthCode else: raise errors.TwoFactorAuthCodeRequired user.last_login = datetime_now() return Sessions.new(tid, user.id, user.tid, user.role, user.password_change_needed, user.two_factor_enable, crypto_prv_key)
def login_whistleblower(session, tid, receipt): """ login_whistleblower returns a session """ x = None algorithms = [x[0] for x in session.query(WhistleblowerTip.hash_alg).filter(WhistleblowerTip.tid == tid).distinct()] if algorithms: hashes = [] for alg in algorithms: hashes.append(GCE.hash_password(receipt, State.tenant_cache[tid].receipt_salt, alg)) x = session.query(WhistleblowerTip, InternalTip) \ .filter(WhistleblowerTip.receipt_hash.in_(hashes), WhistleblowerTip.tid == tid, InternalTip.id == WhistleblowerTip.id, InternalTip.tid == WhistleblowerTip.tid).one_or_none() if x is None: log.debug("Whistleblower login: Invalid receipt") Settings.failed_login_attempts += 1 raise errors.InvalidAuthentication wbtip = x[0] itip = x[1] itip.wb_last_access = datetime_now() crypto_prv_key = '' if State.tenant_cache[1].encryption and wbtip.crypto_prv_key: user_key = GCE.derive_key(receipt.encode('utf-8'), State.tenant_cache[tid].receipt_salt) crypto_prv_key = GCE.symmetric_decrypt(user_key, wbtip.crypto_prv_key) return Sessions.new(tid, wbtip.id, 'whistleblower', False, crypto_prv_key)
def request(self, body='', uri=b'https://www.globaleaks.org/', user_id=None, role=None, multilang=False, headers=None, client_addr=None, handler_cls=None, attached_file=None, kwargs={}): """ Constructs a handler for preforming mock requests using the bag of params described below. """ from globaleaks.rest import api if handler_cls is None: handler_cls = self._handler request = forge_request(uri=uri, headers=headers, body=body, client_addr=client_addr, method=b'GET') x = api.APIResourceWrapper() x.preprocess(request) if not getattr(handler_cls, 'decorated', False): for method in ['get', 'post', 'put', 'delete']: if getattr(handler_cls, method, None) is not None: decorators.decorate_method(handler_cls, method) handler_cls.decorated = True handler = handler_cls(self.state, request, **kwargs) if multilang: request.language = None if user_id is None and role is not None: if role == 'admin': user_id = self.dummyAdminUser['id'] elif role == 'receiver': user_id = self.dummyReceiverUser_1['id'] elif role == 'custodian': user_id = self.dummyCustodianUser['id'] if headers is not None and headers.get('x-session', None) is not None: handler.request.headers[b'x-session'] = headers.get( 'x-session').encode() elif role is not None: session = Sessions.new(1, user_id, role, False, USER_PRV_KEY) handler.request.headers[b'x-session'] = session.id.encode() if handler.upload_handler: handler.uploaded_file = self.get_dummy_file( u'upload.raw', attached_file) return handler
def login(session, tid, username, password, authcode, client_using_tor, client_ip): """ Login transaction for users' access :param session: An ORM session :param tid: A tenant ID :param username: A provided username :param password: A provided password :param authcode: A provided authcode :param client_using_tor: A boolean signaling Tor usage :param client_ip: The client IP :return: Returns a user session in case of success """ user = None for u in session.query(User).filter(User.username == username, User.state == 'enabled', User.tid == tid): if GCE.check_password(u.hash_alg, password, u.salt, u.password): user = u break if user is None: log.debug("Login: Invalid credentials") login_error(tid) connection_check(tid, client_ip, user.role, client_using_tor) crypto_prv_key = '' if user.crypto_prv_key: user_key = GCE.derive_key(password.encode(), user.salt) crypto_prv_key = GCE.symmetric_decrypt( user_key, Base64Encoder.decode(user.crypto_prv_key)) elif State.tenant_cache[tid].encryption: # Force the password change on which the user key will be created user.password_change_needed = True # Require password change if password change threshold is exceeded if State.tenant_cache[tid].password_change_period > 0 and \ user.password_change_date < datetime_now() - timedelta(days=State.tenant_cache[tid].password_change_period): user.password_change_needed = True if user.two_factor_enable: if authcode != '': # RFC 6238: step size 30 sec; valid_window = 1; total size of the window: 1.30 sec if not pyotp.TOTP(user.two_factor_secret).verify(authcode, valid_window=1): raise errors.InvalidTwoFactorAuthCode else: raise errors.TwoFactorAuthCodeRequired user.last_login = datetime_now() return Sessions.new(tid, user.id, user.tid, user.role, user.password_change_needed, user.two_factor_enable, crypto_prv_key, user.crypto_escrow_prv_key)
def login(session, tid, username, password, authcode, client_using_tor, client_ip): """ login returns a session """ user = None users = session.query(User).filter(User.username == username, User.state != u'disabled', UserTenant.user_id == User.id, UserTenant.tenant_id == tid).distinct() for u in users: if GCE.check_password(u.hash_alg, password, u.salt, u.password): user = u break if user is None: log.debug("Login: Invalid credentials") Settings.failed_login_attempts += 1 raise errors.InvalidAuthentication connection_check(client_ip, tid, user.role, client_using_tor) if State.tenant_cache[ 1].two_factor_auth and user.last_login != datetime_null(): token = TwoFactorTokens.get(user.id) if token is not None and authcode != '': if token.token == authcode: TwoFactorTokens.revoke(user.id) else: raise errors.InvalidTwoFactorAuthCode elif token is None and authcode == '': token = TwoFactorTokens.new(user.id) data = {'type': '2fa', 'authcode': str(token.token)} data['node'] = db_admin_serialize_node(session, tid, user.language) data['notification'] = db_get_notification(session, tid, user.language) subject, body = Templating().get_mail_subject_and_body(data) State.sendmail(1, user.mail_address, subject, body) raise errors.TwoFactorAuthCodeRequired else: raise errors.TwoFactorAuthCodeRequired user.last_login = datetime_now() crypto_prv_key = '' if State.tenant_cache[1].encryption and user.crypto_prv_key: user_key = GCE.derive_key(password.encode('utf-8'), user.salt) crypto_prv_key = GCE.symmetric_decrypt(user_key, user.crypto_prv_key) return Sessions.new(tid, user.id, user.role, user.password_change_needed, crypto_prv_key)
def get(self, tid): tid = int(tid) check = yield check_tenant_auth_switch(self.current_user, tid) if check: session = Sessions.new(tid, self.current_user.user_id, self.current_user.user_role, self.current_user.pcn, self.current_user.cc) returnValue({ 'redirect': '/t/%d/#/login?token=%s' % (tid, session.id) })
def get(self, tid): tid = int(tid) check = yield check_tenant_auth_switch(self.current_user, tid) if check: session = Sessions.new(tid, self.current_user.user_id, self.current_user.user_tid, self.current_user.user_role, self.current_user.pcn, self.current_user.cc) returnValue({ 'redirect': '/t/%d/#/login?token=%s' % (tid, session.id) })
def validate_password_reset(session, reset_token, auth_code, recovery_key): """ Retrieves a user given a password reset validation token :param session: An ORM session :param reset_token: A reset token :param auth_code: A two factor authentication code (optional) :param recovery_key: An encryption recovery key (optional) :return: A descriptor describing the result of the operation """ now = datetime.now() prv_key = '' user = session.query(models.User).filter( models.User.reset_password_token == reset_token, models.User.reset_password_date >= now - timedelta(hours=72) ).one_or_none() # If the authentication token is invalid if user is None: return {'status': 'invalid_reset_token_provided'} # If encryption is enabled require the recovery key if user.crypto_prv_key: try: x = State.TempKeys.pop(user.id, None) if x: enc_key = GCE.derive_key(reset_token.encode(), user.salt) prv_key = GCE.symmetric_decrypt(enc_key, Base64Encoder.decode(x.key)) else: recovery_key = recovery_key.replace('-', '').upper() + '====' recovery_key = Base32Encoder.decode(recovery_key.encode()) prv_key = GCE.symmetric_decrypt(recovery_key, Base64Encoder.decode(user.crypto_bkp_key)) except: return {'status': 'require_recovery_key'} elif user.two_factor_enable: two_factor_secret = user.two_factor_secret if not pyotp.TOTP(two_factor_secret).verify(auth_code, valid_window=1): return {'status': 'require_two_factor_authentication'} # Token is used, void it out user.reset_password_token = None user.reset_password_date = now # Require password change user.password_change_needed = True session = Sessions.new(user.tid, user.id, user.tid, user.role, user.password_change_needed, user.two_factor_enable, prv_key, user.crypto_escrow_prv_key) return {'status': 'success', 'token': session.id}
def login(session, tid, username, password, client_using_tor, client_ip): """ login returns a session """ user = None users = session.query(User).filter(User.username == username, User.state != u'disabled', UserTenant.user_id == User.id, UserTenant.tenant_id == tid).distinct() for u in users: if GCE.check_password(u.hash_alg, password, u.salt, u.password): user = u break if user is None: log.debug("Login: Invalid credentials") Settings.failed_login_attempts += 1 raise errors.InvalidAuthentication if not client_using_tor and not State.tenant_cache[tid]['https_' + user.role]: log.err("Denied login request over Web for role '%s'" % user.role) raise errors.TorNetworkRequired # Check if we're doing IP address checks today if State.tenant_cache[tid]['ip_filter_authenticated_enable']: ip_networks = parse_csv_ip_ranges_to_ip_networks( State.tenant_cache[tid]['ip_filter_authenticated']) if isinstance(client_ip, binary_type): client_ip = client_ip.decode() client_ip_obj = ipaddress.ip_address(client_ip) # Safety check, we always allow localhost to log in success = False if client_ip_obj.is_loopback is True: success = True for ip_network in ip_networks: if client_ip_obj in ip_network: success = True if success is not True: raise errors.AccessLocationInvalid user.last_login = datetime_now() crypto_prv_key = '' if State.tenant_cache[1].encryption and user.crypto_prv_key: user_key = GCE.derive_key(password.encode('utf-8'), user.salt) crypto_prv_key = GCE.symmetric_decrypt(user_key, user.crypto_prv_key) return Sessions.new(tid, user.id, user.role, user.password_change_needed, crypto_prv_key)
def get(self, tid): check = yield check_tenant_auth_switch(self.current_user, tid) if check: session = Sessions.new(tid, self.current_user.user_id, self.current_user.user_role, self.current_user.pcn, self.current_user.cc) returnValue({ 'redirect': 'https://%s/#/login?token=%s' % (State.tenant_cache[tid].hostname, session.id) })
def get(self, tid): if self.request.tid != 1: raise errors.InvalidAuthentication tid = int(tid) session = Sessions.new(tid, self.current_user.user_id, self.current_user.user_tid, self.current_user.user_role, False, True, self.current_user.cc, self.current_user.ek, True) return {'redirect': '/t/%d/#/login?token=%s' % (tid, session.id)}
def request(self, body='', uri=b'https://www.globaleaks.org/', user_id=None, role=None, multilang=False, headers=None, client_addr=None, handler_cls=None, attached_file=None, kwargs={}): """ Constructs a handler for preforming mock requests using the bag of params described below. """ from globaleaks.rest import api if handler_cls is None: handler_cls = self._handler request = forge_request(uri=uri, headers=headers, body=body, client_addr=client_addr, method=b'GET') x = api.APIResourceWrapper() x.preprocess(request) if not getattr(handler_cls, 'decorated', False): for method in ['get', 'post', 'put', 'delete']: if getattr(handler_cls, method, None) is not None: decorators.decorate_method(handler_cls, method) handler_cls.decorated = True handler = handler_cls(self.state, request, **kwargs) if multilang: request.language = None if user_id is None and role is not None: if role == 'admin': user_id = self.dummyAdminUser['id'] elif role == 'receiver': user_id = self.dummyReceiverUser_1['id'] elif role == 'custodian': user_id = self.dummyCustodianUser['id'] if headers is not None and headers.get('x-session', None) is not None: handler.request.headers[b'x-session'] = headers.get('x-session').encode() elif role is not None: session = Sessions.new(1, user_id, role, False, USER_PRV_KEY) handler.request.headers[b'x-session'] = session.id.encode() if handler.upload_handler: handler.uploaded_file = self.get_dummy_file(u'upload.raw', attached_file) return handler
def validate_password_reset(session, tid, reset_token): """Retrieves a user given a password reset validation token""" user = session.query(models.User).filter( models.User.reset_password_token == reset_token, models.User.reset_password_date >= datetime.now() - timedelta(hours=72) ).one_or_none() if user is None: return # Token is used, void it out user.reset_password_token = None user.reset_password_date = datetime_now() user.password_change_needed = True session = Sessions.new(tid, user.id, user.role, user.password_change_needed) return session.id
def validate_password_reset(session, tid, reset_token): """Retrieves a user given a password reset validation token""" user = session.query(models.User).filter( models.User.reset_password_token == reset_token, models.User.reset_password_date >= datetime.now() - timedelta(hours=72) ).one_or_none() if user is None: return # Token is used, void it out user.reset_password_token = None user.reset_password_date = datetime_now() user.password_change_needed = True session = Sessions.new(tid, user.id, user.role, user.password_change_needed, '') return session.id
def validate_password_reset(session, tid, reset_token, auth_code, recovery_key): """Retrieves a user given a password reset validation token""" now = datetime.now() prv_key = '' user = session.query(models.User).filter( models.User.reset_password_token == reset_token, models.User.reset_password_date >= now - timedelta(hours=72)).one_or_none() # If the authentication token is invalid if user is None: return {'status': 'invalid_reset_token_provided'} # If encryption is enabled require the recovery key if user.crypto_prv_key: try: recovery_key = recovery_key.replace('-', '').upper() + '====' recovery_key = Base32Encoder().decode(recovery_key.encode('utf-8')) prv_key = GCE.symmetric_decrypt(recovery_key, user.crypto_bkp_key) except: return {'status': 'require_recovery_key'} elif user.two_factor_enable: two_factor_secret = user.two_factor_secret.decode('utf-8') if not pyotp.TOTP(two_factor_secret).verify(auth_code, valid_window=1): return {'status': 'require_two_factor_authentication'} # Token is used, void it out user.reset_password_token = None user.reset_password_date = now user.password_change_needed = True session = Sessions.new(tid, user.id, user.tid, user.role, user.password_change_needed, user.two_factor_enable, prv_key) return {'status': 'success', 'token': session.id}
def setUp(self): yield helpers.TestHandlerWithPopulatedDB.setUp(self) session = Sessions.new(1, self.dummyReceiver_1['id'], 'receiver', False, '') self.token = session.id
def setUp(self): yield helpers.TestHandlerWithPopulatedDB.setUp(self) session = Sessions.new(1, self.dummyReceiver_1['id'], 1, 'receiver', False, False, '') self.token = session.id
def login(session, tid, username, password, authcode, client_using_tor, client_ip): """ Login transaction for users' access :param session: An ORM session :param tid: A tenant ID :param username: A provided username :param password: A provided password :param authcode: A provided authcode :param client_using_tor: A boolean signaling Tor usage :param client_ip: The client IP :return: Returns a user session in case of success """ user = None users = session.query(User).filter(User.username == username, User.state != 'disabled', User.tid == tid).distinct() for u in users: if GCE.check_password(u.hash_alg, password, u.salt, u.password): user = u break # Fix for issue: https://github.com/globaleaks/GlobaLeaks/issues/2563 if State.tenant_cache[1].creation_date < 1551740400: u_password = '******'' + u.password + '\'' if GCE.check_password(u.hash_alg, password, u.salt, u_password): user = u break if user is None: log.debug("Login: Invalid credentials") Settings.failed_login_attempts += 1 raise errors.InvalidAuthentication connection_check(client_ip, tid, user.role, client_using_tor) crypto_prv_key = '' if user.crypto_prv_key: user_key = GCE.derive_key(password.encode(), user.salt) crypto_prv_key = GCE.symmetric_decrypt( user_key, Base64Encoder.decode(user.crypto_prv_key)) elif State.tenant_cache[tid].encryption: # Force the password change on which the user key will be created user.password_change_needed = True if user.two_factor_enable: if authcode != '': # RFC 6238: step size 30 sec; valid_window = 1; total size of the window: 1.30 sec if not pyotp.TOTP(user.two_factor_secret).verify(authcode, valid_window=1): raise errors.InvalidTwoFactorAuthCode else: raise errors.TwoFactorAuthCodeRequired user.last_login = datetime_now() return Sessions.new(tid, user.id, user.tid, user.role, user.password_change_needed, user.two_factor_enable, crypto_prv_key, user.crypto_escrow_prv_key)
def login(session, tid, username, password, authcode, client_using_tor, client_ip): """ login returns a session """ user = None users = session.query(User).filter(User.username == username, User.state != u'disabled', UserTenant.user_id == User.id, UserTenant.tenant_id == tid).distinct() for u in users: if GCE.check_password(u.hash_alg, password, u.salt, u.password): user = u break # Fix for issue: https://github.com/globaleaks/GlobaLeaks/issues/2563 if State.tenant_cache[1].creation_date < datetime.timestamp(datetime(2019, 5, 3, 0, 0)): u_password = '******'' + u.password + '\'' if GCE.check_password(u.hash_alg, password, u.salt, u_password): user = u break if user is None: log.debug("Login: Invalid credentials") Settings.failed_login_attempts += 1 raise errors.InvalidAuthentication connection_check(client_ip, tid, user.role, client_using_tor) if State.tenant_cache[1].two_factor_auth and user.last_login != datetime_null(): token = TwoFactorTokens.get(user.id) if token is not None and authcode != '': if token.token == authcode: TwoFactorTokens.revoke(user.id) else: raise errors.InvalidTwoFactorAuthCode elif token is None and authcode == '': token = TwoFactorTokens.new(user.id) data = { 'type': '2fa', 'authcode': str(token.token) } data['node'] = db_admin_serialize_node(session, tid, user.language) data['notification'] = db_get_notification(session, tid, user.language) subject, body = Templating().get_mail_subject_and_body(data) State.sendmail(1, user.mail_address, subject, body) raise errors.TwoFactorAuthCodeRequired else: raise errors.TwoFactorAuthCodeRequired user.last_login = datetime_now() crypto_prv_key = '' if State.tenant_cache[1].encryption and user.crypto_prv_key: user_key = GCE.derive_key(password.encode('utf-8'), user.salt) crypto_prv_key = GCE.symmetric_decrypt(user_key, user.crypto_prv_key) return Sessions.new(tid, user.id, user.role, user.password_change_needed, crypto_prv_key)