def _create_user(self, username, set_password=False): """Create a user in the database. Args: username: Username (string) set_password: Boolean value to decide if a password should be set Returns: A user (instance of timesketch.models.user.User) """ user = User(username=username) if set_password: user.set_password(plaintext=u'test', rounds=4) self._commit_to_database(user) return user
def _create_mock_event(event_id, quantity, time_diffs=None, source_attrs=None): """ Returns an instance of Event, based on the MockDataStore event_dict example. Args: event_id: Desired ID for the Event. quantity: The number of Events to be generated. time_diffs: A list of time differences between the generated Events. source_attrs: Dictionary of attributes to add to the source of the generated events. Returns: A generator of Event objects. """ if not time_diffs: time_diffs = [0] if quantity < 0: quantity = abs(quantity) # If the list of time differences is too small to be compatible # with the quantity of events, then extend the list with the last # value for as many items as necessary. if quantity - len(time_diffs) > 0: time_diffs.extend([time_diffs[len(time_diffs) - 1]] * (quantity - len(time_diffs))) # Setup for Event object initialisation ds = MockDataStore('test', 0) user = User('test_user') sketch = Sketch('test_sketch', 'description', user) label = sketch.Label(label='Test label', user=user) sketch.labels.append(label) event_timestamp = 1410895419859714 event_template = ds.get_event('test', 'test') for i in range(quantity): eventObj = _create_eventObj(ds, sketch, event_template, event_id, event_timestamp, source_attrs) yield eventObj # adding extra events after every requested event for better # simulation of real timeline data i.e. working with a larger # dataset for _ in range(100): event_timestamp += 1 event_id += 1 eventObj = _create_eventObj(ds, sketch, event_template, event_id, event_timestamp, source_attrs) yield eventObj event_timestamp += abs(time_diffs[i]) event_id += 1
def run(self, username, password): """Creates the user.""" if not password: password = self.get_password_from_prompt() password = unicode(password.decode(encoding=u'utf-8')) username = unicode(username.decode(encoding=u'utf-8')) user = User.get_or_create(username=username) user.set_password(plaintext=password) db_session.add(user) db_session.commit() sys.stdout.write(u'User {0:s} created/updated\n'.format(username))
def run(self, username, password): """Creates the user.""" if not password: password = self.get_password_from_prompt() if not isinstance(password, six.text_type): password = codecs.decode(password, 'utf-8') username = codecs.decode(username, 'utf-8') user = User.get_or_create(username=username) user.set_password(plaintext=password) db_session.add(user) db_session.commit() sys.stdout.write('User {0:s} created/updated\n'.format(username))
def create_user(username, password=None): """Create a user.""" def get_password_from_prompt(): """Get password from the command line prompt.""" first_password = click.prompt("Enter password", hide_input=True, type=str) second_password = click.prompt("Enter password again", hide_input=True, type=str) if first_password != second_password: print("Passwords don't match, try again.") get_password_from_prompt() return first_password if not password: password = get_password_from_prompt() user = User.get_or_create(username=username) user.set_password(plaintext=password) db_session.add(user) db_session.commit() print(f"User {username, password} created/updated")
def test_get_event_data(self): """Test getEventData returns the correct values.""" user = User('test_user') sketch = Sketch('test_sketch', 'description', user) label = sketch.Label(label='Test label', user=user) sketch.labels.append(label) index = 'test_index' sketch_id = 1 for analyzer_class in self.analyzer_classes: analyzer = analyzer_class['class'](index, sketch_id) datastore = analyzer.datastore event_dict = copy.deepcopy(MockDataStore.event_dict) event_dict['_source'].update({'xml_string': xml_string1}) event_obj = Event(event_dict, datastore, sketch) username = analyzer.getEventData(event_obj, 'TargetUserName') logon_id = analyzer.getEventData(event_obj, 'TargetLogonId') self.assertEqual(username, 'USER_1') self.assertEqual(logon_id, '0x0000000000000001')
def test_get_event_data(self): """Test getEventData returns the correct values.""" user = User("test_user") sketch = Sketch("test_sketch", "description", user) label = sketch.Label(label="Test label", user=user) sketch.labels.append(label) index = "test_index" sketch_id = 1 for analyzer_class in self.analyzer_classes: analyzer = analyzer_class["class"](index, sketch_id) datastore = analyzer.datastore event_dict = copy.deepcopy(MockDataStore.event_dict) event_dict["_source"].update({"xml_string": xml_string1}) event_obj = Event(event_dict, datastore, sketch) username = analyzer.getEventData(event_obj, "TargetUserName") logon_id = analyzer.getEventData(event_obj, "TargetLogonId") self.assertEqual(username, "USER_1") self.assertEqual(logon_id, "0x0000000000000001")
def login(): """Handler for the login page view. There are two ways of authentication. 1) If Single Sign On (SSO) is enabled in configuration and the environment variable is present, e.g. REMOTE_USER then the system will get or create the user object and setup a session for the user. 2) Local authentication is used if SSO login is not enabled. This will authenticate the user against the local user database. Returns: Redirect if authentication is successful or template with context otherwise. """ form = UsernamePasswordForm() # SSO login based on environment variable, e.g. REMOTE_USER. if current_app.config.get(u'SSO_ENABLED', False): remote_user_env = current_app.config.get( u'SSO_USER_ENV_VARIABLE', u'REMOTE_USER') remote_user = request.environ.get(remote_user_env, None) if remote_user: user = User.get_or_create(username=remote_user, name=remote_user) login_user(user) # Login form POST if form.validate_on_submit: user = User.query.filter_by(username=form.username.data).first() if user: if user.check_password(plaintext=form.password.data): login_user(user) if current_user.is_authenticated: return redirect(request.args.get(u'next') or u'/') return render_template(u'user/login.html', form=form)
def login(): """Handler for the login page view. There are two ways of authentication. 1) If Single Sign On (SSO) is enabled in configuration and the environment variable is present, e.g. REMOTE_USER then the system will get or create the user object and setup a session for the user. 2) Local authentication is used if SSO login is not enabled. This will authenticate the user against the local user database. Returns: Redirect if authentication is successful or template with context otherwise. """ form = UsernamePasswordForm() # SSO login based on environment variable, e.g. REMOTE_USER. if current_app.config.get(u'SSO_ENABLED', False): remote_user_env = current_app.config.get(u'SSO_USER_ENV_VARIABLE', u'REMOTE_USER') remote_user = request.environ.get(remote_user_env, None) if remote_user: user = User.get_or_create(username=remote_user, name=remote_user) login_user(user) # Login form POST if form.validate_on_submit(): user = User.query.filter_by(username=form.username.data).first() if user: if user.check_password(plaintext=form.password.data): login_user(user) if current_user.is_authenticated: return redirect(request.args.get(u'next') or u'/') return render_template(u'user/login.html', form=form)
def run(self, import_location, export_location): """Export/Import search templates to/from file. Args: import_location: Path to the yaml file to import templates. export_location: Path to the yaml file to export templates. """ if export_location: search_templates = [] for search_template in SearchTemplate.query.all(): labels = [] for label in search_template.labels: if label.label.startswith('supported_os:'): labels.append(label.label.replace('supported_os:', '')) search_templates.append({ 'name': search_template.name, 'query_string': search_template.query_string, 'query_dsl': search_template.query_dsl, 'supported_os': labels }) with open(export_location, 'w') as fh: yaml.safe_dump(search_templates, stream=fh) if import_location: try: with open(import_location, 'rb') as fh: search_templates = yaml.safe_load(fh) except IOError as e: sys.stdout.write('Unable to open file: {0!s}\n'.format(e)) sys.exit(1) for search_template in search_templates: name = search_template['name'] query_string = search_template['query_string'] query_dsl = search_template['query_dsl'] # Skip search template if already exits. if SearchTemplate.query.filter_by(name=name).first(): continue imported_template = SearchTemplate(name=name, user=User(None), query_string=query_string, query_dsl=query_dsl) # Add supported_os labels. for supported_os in search_template['supported_os']: label_name = 'supported_os:{0:s}'.format(supported_os) label = SearchTemplate.Label.get_or_create( label=label_name, user=None) imported_template.labels.append(label) # Set flag to identify local vs import templates. remote_flag = SearchTemplate.Label.get_or_create( label='remote_template', user=None) imported_template.labels.append(remote_flag) db_session.add(imported_template) db_session.commit()
def login(): """Handler for the login page view. There are three ways of authentication. 1) Google Cloud Identity-Aware Proxy. 2) If Single Sign On (SSO) is enabled in the configuration and the environment variable is present, e.g. REMOTE_USER then the system will get or create the user object and setup a session for the user. 3) Local authentication is used if SSO login is not enabled. This will authenticate the user against the local user database. Returns: Redirect if authentication is successful or template with context otherwise. """ # Google OpenID Connect authentication. if current_app.config.get('GOOGLE_OIDC_ENABLED', False): hosted_domain = current_app.config.get('GOOGLE_OIDC_HOSTED_DOMAIN') return redirect(get_oauth2_authorize_url(hosted_domain)) # Google Identity-Aware Proxy authentication (using JSON Web Tokens) if current_app.config.get('GOOGLE_IAP_ENABLED', False): encoded_jwt = request.environ.get('HTTP_X_GOOG_IAP_JWT_ASSERTION', None) if encoded_jwt: expected_audience = current_app.config.get('GOOGLE_IAP_AUDIENCE') expected_issuer = current_app.config.get('GOOGLE_IAP_ISSUER') algorithm = current_app.config.get('GOOGLE_IAP_ALGORITHM') url = current_app.config.get('GOOGLE_IAP_PUBLIC_KEY_URL') try: public_key = get_public_key_for_jwt(encoded_jwt, url) decoded_jwt = decode_jwt(encoded_jwt, public_key, algorithm, expected_audience) validate_jwt(decoded_jwt, expected_issuer) email = decoded_jwt.get('email') if email: user = User.get_or_create(username=email, name=email) login_user(user) except (ImportError, NameError, UnboundLocalError): raise except (JwtValidationError, JwtKeyError, Exception) as e: # pylint: disable=broad-except current_app.logger.error('{}'.format(e)) # SSO login based on environment variable, e.g. REMOTE_USER. if current_app.config.get('SSO_ENABLED', False): remote_user_env = current_app.config.get('SSO_USER_ENV_VARIABLE', 'REMOTE_USER') sso_group_env = current_app.config.get('SSO_GROUP_ENV_VARIABLE', None) remote_user = request.environ.get(remote_user_env, None) if remote_user: user = User.get_or_create(username=remote_user, name=remote_user) login_user(user) # If we get groups from the SSO system create the group(s) in # Timesketch and add/remove the user from it. if sso_group_env: groups_string = request.environ.get(sso_group_env, '') separator = current_app.config.get('SSO_GROUP_SEPARATOR', ';') not_member_sign = current_app.config.get( 'SSO_GROUP_NOT_MEMBER_SIGN', None) for group_name in groups_string.split(separator): remove_group = False if not_member_sign: remove_group = group_name.startswith(not_member_sign) group_name = group_name.lstrip(not_member_sign) # Get or create the group in the Timesketch database. group = Group.get_or_create(name=group_name) if remove_group: if group in user.groups: user.groups.remove(group) else: if group not in user.groups: user.groups.append(group) # Commit the changes to the database. db_session.commit() # Login form POST form = UsernamePasswordForm() if form.validate_on_submit: user = User.query.filter_by(username=form.username.data).first() if user: if user.check_password(plaintext=form.password.data): login_user(user) # Log the user in and setup the session. if current_user.is_authenticated: return redirect(request.args.get('next') or '/') return render_template('login.html', form=form)
def google_openid_connect(): """Handler for the Google OpenID Connect callback. Reference: https://developers.google.com/identity/protocols/OpenIDConnect Returns: Redirect response. """ error = request.args.get('error', None) if error: current_app.logger.error('OAuth2 flow error: {}'.format(error)) return abort(HTTP_STATUS_CODE_BAD_REQUEST, 'OAuth2 flow error: {0!s}'.format(error)) try: code = request.args['code'] client_csrf_token = request.args.get('state') server_csrf_token = session[CSRF_KEY] except KeyError as e: return abort(HTTP_STATUS_CODE_BAD_REQUEST, 'Client CSRF error, no CSRF key stored') if client_csrf_token != server_csrf_token: return abort(HTTP_STATUS_CODE_BAD_REQUEST, 'Invalid CSRF token') try: encoded_jwt = get_encoded_jwt_over_https(code) except JwtFetchError as e: return abort(HTTP_STATUS_CODE_BAD_REQUEST, 'Jwt Fetch error, {0!s}'.format(e)) try: discovery_document = get_oauth2_discovery_document() except DiscoveryDocumentError as e: return abort( HTTP_STATUS_CODE_BAD_REQUEST, 'Unable to discover document, with error: {0!s}'.format(e)) algorithm = discovery_document['id_token_signing_alg_values_supported'][0] expected_audience = current_app.config.get('GOOGLE_OIDC_CLIENT_ID') expected_domain = current_app.config.get('GOOGLE_OIDC_HOSTED_DOMAIN') expected_issuer = discovery_document['issuer'] # Fetch the public key and try to validate the JWT. try: public_key = get_public_key_for_jwt(encoded_jwt, discovery_document['jwks_uri']) decoded_jwt = decode_jwt(encoded_jwt, public_key, algorithm, expected_audience) validate_jwt(decoded_jwt, expected_issuer, expected_domain) except (JwtValidationError, JwtKeyError) as e: current_app.logger.error('{}'.format(e)) return abort(HTTP_STATUS_CODE_UNAUTHORIZED, 'Unable to validate request, with error: {0!s}'.format(e)) validated_email = decoded_jwt.get('email') user_whitelist = current_app.config.get('GOOGLE_OIDC_USER_WHITELIST') # Check if the authenticating user is on the whitelist. if user_whitelist: if validated_email not in user_whitelist: return abort(HTTP_STATUS_CODE_UNAUTHORIZED, 'Unauthorized request, user not in whitelist') user = User.get_or_create(username=validated_email, name=validated_email) login_user(user) # Log the user in and setup the session. if current_user.is_authenticated: return redirect(request.args.get('next') or '/') return abort(HTTP_STATUS_CODE_BAD_REQUEST, 'User is not authenticated.')
def validate_api_token(): """Handler for logging in using an authenticated session for the API. Returns: A simple page indicating the user is authenticated. """ try: token = oauth2.rfc6749.tokens.get_token_from_header(request) except AttributeError: token = None if not token: return abort(HTTP_STATUS_CODE_UNAUTHORIZED, 'Request not authenticated.') id_token = request.args.get('id_token') if not id_token: return abort(HTTP_STATUS_CODE_UNAUTHORIZED, 'No ID token supplied.') client_id = current_app.config.get('GOOGLE_OIDC_API_CLIENT_ID') if not client_id: return abort( HTTP_STATUS_CODE_BAD_REQUEST, 'No OIDC API client ID defined in the configuration file.') # Authenticating session, see more details here: # https://www.oauth.com/oauth2-servers/signing-in-with-google/\ # verifying-the-user-info/ # Sending a request to Google to verify that the access token # is valid, to be able to validate the session. data = {'access_token': token} bearer_token_response = requests.post(TOKEN_URI, data=data) if bearer_token_response.status_code != HTTP_STATUS_CODE_OK: return abort(HTTP_STATUS_CODE_BAD_REQUEST, 'Unable to validate access token.') bearer_token_json = bearer_token_response.json() data = {'id_token': id_token} token_response = requests.post(TOKEN_URI, data=data) token_json = token_response.json() verified = token_json.get('email_verified', False) if not verified: return abort(HTTP_STATUS_CODE_UNAUTHORIZED, 'Session not authenticated or account not verified') if bearer_token_json.get('azp', 'a') != token_json.get('azp', 'x'): return abort( HTTP_STATUS_CODE_UNAUTHORIZED, 'Auth token and client tokens don\'t match, azp differs.') if bearer_token_json.get('email', 'a') != token_json.get('email', 'b'): return abort( HTTP_STATUS_CODE_UNAUTHORIZED, 'Auth token and client tokens don\'t match, email differs.') try: discovery_document = get_oauth2_discovery_document() except DiscoveryDocumentError as e: return abort( HTTP_STATUS_CODE_BAD_REQUEST, 'Unable to discover document, with error: {0!s}'.format(e)) expected_issuer = discovery_document['issuer'] try: validate_jwt(token_json, expected_issuer) except (ImportError, NameError, UnboundLocalError): raise except (JwtValidationError, JwtKeyError, Exception) as e: # pylint: disable=broad-except return abort( HTTP_STATUS_CODE_UNAUTHORIZED, 'Unable to validate the JWT token, with error: {0!s}.'.format(e)) read_client_id = token_json.get('aud', '') if read_client_id != client_id: return abort( HTTP_STATUS_CODE_UNAUTHORIZED, 'Client ID {0:s} does not match server configuration for ' 'client'.format(read_client_id)) read_scopes = bearer_token_json.get('scope', '').split() if not set(read_scopes) == set(SCOPES): return abort( HTTP_STATUS_CODE_UNAUTHORIZED, 'Client scopes differ from what they should be (email, openid, ' 'profile) = {} VS {}'.format(SCOPES, read_scopes)) validated_email = token_json.get('email') # Check if the authenticating user is part of the allowed domains. domain_whitelist = current_app.config.get('GOOGLE_OIDC_HOSTED_DOMAIN') if domain_whitelist: _, _, domain = validated_email.partition('@') if domain.lower() != domain_whitelist.lower(): return abort( HTTP_STATUS_CODE_UNAUTHORIZED, 'Domain {0:s} is not allowed to authenticate against this ' 'instance.'.format(domain)) user_whitelist = current_app.config.get('GOOGLE_OIDC_USER_WHITELIST') # Check if the authenticating user is on the whitelist. if user_whitelist: if validated_email not in user_whitelist: return abort(HTTP_STATUS_CODE_UNAUTHORIZED, 'Unauthorized request, user not in whitelist') user = User.get_or_create(username=validated_email, name=validated_email) login_user(user) # Log the user in and setup the session. if current_user.is_authenticated: return """ <h1>Authenticated</h1> """ return abort(HTTP_STATUS_CODE_BAD_REQUEST, 'User is not authenticated.')
def login(): """Handler for the login page view. There are three ways of authentication. 1) Google Cloud Identity-Aware Proxy. 2) If Single Sign On (SSO) is enabled in the configuration and the environment variable is present, e.g. REMOTE_USER then the system will get or create the user object and setup a session for the user. 3) Local authentication is used if SSO login is not enabled. This will authenticate the user against the local user database. Returns: Redirect if authentication is successful or template with context otherwise. """ # Google OpenID Connect authentication. if current_app.config.get('GOOGLE_OIDC_ENABLED', False): hosted_domain = current_app.config.get('GOOGLE_OIDC_HOSTED_DOMAIN') return redirect(get_oauth2_authorize_url(hosted_domain)) # Google Identity-Aware Proxy authentication (using JSON Web Tokens) if current_app.config.get('GOOGLE_IAP_ENABLED', False): encoded_jwt = request.environ.get( 'HTTP_X_GOOG_IAP_JWT_ASSERTION', None) if encoded_jwt: expected_audience = current_app.config.get('GOOGLE_IAP_AUDIENCE') expected_issuer = current_app.config.get('GOOGLE_IAP_ISSUER') algorithm = current_app.config.get('GOOGLE_IAP_ALGORITHM') url = current_app.config.get('GOOGLE_IAP_PUBLIC_KEY_URL') try: public_key = get_public_key_for_jwt(encoded_jwt, url) validated_jwt = validate_jwt( encoded_jwt, public_key, algorithm, expected_audience, expected_issuer) email = validated_jwt.get('email') if email: user = User.get_or_create(username=email, name=email) login_user(user) except (ImportError, NameError, UnboundLocalError): # pylint: disable=try-except-raise raise except (JwtValidationError, JwtKeyError, Exception) as e: # pylint: disable=broad-except current_app.logger.error('{}'.format(e)) # SSO login based on environment variable, e.g. REMOTE_USER. if current_app.config.get('SSO_ENABLED', False): remote_user_env = current_app.config.get('SSO_USER_ENV_VARIABLE', 'REMOTE_USER') sso_group_env = current_app.config.get('SSO_GROUP_ENV_VARIABLE', None) remote_user = request.environ.get(remote_user_env, None) if remote_user: user = User.get_or_create(username=remote_user, name=remote_user) login_user(user) # If we get groups from the SSO system create the group(s) in # Timesketch and add/remove the user from it. if sso_group_env: groups_string = request.environ.get(sso_group_env, '') separator = current_app.config.get('SSO_GROUP_SEPARATOR', ';') not_member_sign = current_app.config.get( 'SSO_GROUP_NOT_MEMBER_SIGN', None) for group_name in groups_string.split(separator): remove_group = False if not_member_sign: remove_group = group_name.startswith(not_member_sign) group_name = group_name.lstrip(not_member_sign) # Get or create the group in the Timesketch database. group = Group.get_or_create(name=group_name) if remove_group: if group in user.groups: user.groups.remove(group) else: if group not in user.groups: user.groups.append(group) # Commit the changes to the database. db_session.commit() # Login form POST form = UsernamePasswordForm() if form.validate_on_submit: user = User.query.filter_by(username=form.username.data).first() if user: if user.check_password(plaintext=form.password.data): login_user(user) # Log the user in and setup the session. if current_user.is_authenticated: return redirect(request.args.get('next') or '/') return render_template('user/login.html', form=form)
def google_openid_connect(): """Handler for the Google OpenID Connect callback. Reference: https://developers.google.com/identity/protocols/OpenIDConnect Returns: Redirect response. """ error = request.args.get('error', None) if error: current_app.logger.error('OAuth2 flow error: {}'.format(error)) return abort(HTTP_STATUS_CODE_BAD_REQUEST) try: code = request.args['code'] client_csrf_token = request.args.get('state') server_csrf_token = session[CSRF_KEY] except KeyError: return abort(HTTP_STATUS_CODE_BAD_REQUEST) if client_csrf_token != server_csrf_token: return abort(HTTP_STATUS_CODE_BAD_REQUEST, 'Invalid CSRF token') try: encoded_jwt = get_encoded_jwt_over_https(code) except JwtFetchError: return abort(HTTP_STATUS_CODE_BAD_REQUEST) try: discovery_document = get_oauth2_discovery_document() except DiscoveryDocumentError: return abort(HTTP_STATUS_CODE_BAD_REQUEST) algorithm = discovery_document['id_token_signing_alg_values_supported'][0] expected_audience = current_app.config.get('GOOGLE_OIDC_CLIENT_ID') expected_domain = current_app.config.get('GOOGLE_OIDC_HOSTED_DOMAIN') expected_issuer = discovery_document['issuer'] # Fetch the public key and try to validate the JWT. try: public_key = get_public_key_for_jwt( encoded_jwt, discovery_document['jwks_uri']) validated_jwt = validate_jwt( encoded_jwt, public_key, algorithm, expected_audience, expected_issuer, expected_domain) except (JwtValidationError, JwtKeyError) as e: current_app.logger.error('{}'.format(e)) return abort(HTTP_STATUS_CODE_UNAUTHORIZED) validated_email = validated_jwt.get('email') user_whitelist = current_app.config.get('GOOGLE_OIDC_USER_WHITELIST') # Check if the authenticating user is on the whitelist. if user_whitelist: if validated_email not in user_whitelist: return abort(HTTP_STATUS_CODE_UNAUTHORIZED) user = User.get_or_create(username=validated_email, name=validated_email) login_user(user) # Log the user in and setup the session. if current_user.is_authenticated: return redirect(request.args.get('next') or '/') return abort(HTTP_STATUS_CODE_BAD_REQUEST)
def login(): """Handler for the login page view. There are two ways of authentication. 1) If Single Sign On (SSO) is enabled in configuration and the environment variable is present, e.g. REMOTE_USER then the system will get or create the user object and setup a session for the user. 2) Local authentication is used if SSO login is not enabled. This will authenticate the user against the local user database. Returns: Redirect if authentication is successful or template with context otherwise. """ form = UsernamePasswordForm() # SSO login based on environment variable, e.g. REMOTE_USER. if current_app.config.get(u'SSO_ENABLED', False): remote_user_env = current_app.config.get(u'SSO_USER_ENV_VARIABLE', u'REMOTE_USER') sso_group_env = current_app.config.get(u'SSO_GROUP_ENV_VARIABLE', None) remote_user = request.environ.get(remote_user_env, None) if remote_user: user = User.get_or_create(username=remote_user, name=remote_user) login_user(user) # If we get groups from the SSO system create the group(s) in # Timesketch and add/remove the user from it. if sso_group_env: groups_string = request.environ.get(sso_group_env, u'') separator = current_app.config.get(u'SSO_GROUP_SEPARATOR', u';') not_member_sign = current_app.config.get( u'SSO_GROUP_NOT_MEMBER_SIGN', None) for group_name in groups_string.split(separator): remove_group = False if not_member_sign: remove_group = group_name.startswith(not_member_sign) group_name = group_name.lstrip(not_member_sign) # Get or create the group in the Timesketch database. group = Group.get_or_create(name=group_name) if remove_group: if group in user.groups: user.groups.remove(group) else: if group not in user.groups: user.groups.append(group) # Commit the changes to the database. db_session.commit() # Login form POST if form.validate_on_submit: user = User.query.filter_by(username=form.username.data).first() if user: if user.check_password(plaintext=form.password.data): login_user(user) if current_user.is_authenticated: return redirect(request.args.get(u'next') or u'/') return render_template(u'user/login.html', form=form)
def setup_sketch(timeline_name, index_name, username, sketch_id=None): """Use existing sketch or create a new sketch. Args: timeline_name: (str) Name of the Timeline index_name: (str) Name of the index username: (str) Who should own the timeline sketch_id: (str) Optional sketch_id to add timeline to Returns: (tuple) sketch ID and timeline ID as integers """ with app.app_context(): user = User.get_or_create(username=username) sketch = None if sketch_id: try: sketch = Sketch.query.get_with_acl(sketch_id, user=user) logger.info('Using existing sketch: {} ({})'.format( sketch.name, sketch.id)) except Forbidden: pass if not (sketch or sketch_id): # Create a new sketch. sketch_name = 'Turbinia: {}'.format(timeline_name) sketch = Sketch(name=sketch_name, description=sketch_name, user=user) # Need to commit here to be able to set permissions later. db_session.add(sketch) db_session.commit() sketch.grant_permission(permission='read', user=user) sketch.grant_permission(permission='write', user=user) sketch.grant_permission(permission='delete', user=user) sketch.status.append(sketch.Status(user=None, status='new')) db_session.add(sketch) db_session.commit() logger.info('Created new sketch: {} ({})'.format( sketch.name, sketch.id)) searchindex = SearchIndex.get_or_create( name=timeline_name, description='Created by Turbinia.', user=user, index_name=index_name) searchindex.grant_permission(permission='read', user=user) searchindex.grant_permission(permission='write', user=user) searchindex.grant_permission(permission='delete', user=user) searchindex.set_status('processing') db_session.add(searchindex) db_session.commit() timeline = Timeline(name=searchindex.name, description=searchindex.description, sketch=sketch, user=user, searchindex=searchindex) # If the user doesn't have write access to the sketch then create the # timeline but don't attach it to the sketch. if not sketch.has_permission(user, 'write'): timeline.sketch = None else: sketch.timelines.append(timeline) db_session.add(timeline) db_session.commit() timeline.set_status('processing') return sketch.id, timeline.id
def validate_api_token(): """Handler for logging in using an authenticated session for the API. Returns: A simple page indicating the user is authenticated. """ ALLOWED_CLIENT_IDS = [] try: token = oauth2.rfc6749.tokens.get_token_from_header(request) except AttributeError: token = None if not token: return abort(HTTP_STATUS_CODE_UNAUTHORIZED, "Request not authenticated.") id_token = request.args.get("id_token") if not id_token: return abort(HTTP_STATUS_CODE_UNAUTHORIZED, "No ID token supplied.") client_ids = set() primary_client_id = current_app.config.get("GOOGLE_OIDC_CLIENT_ID") legacy_api_client_id = current_app.config.get("GOOGLE_OIDC_API_CLIENT_ID") api_client_ids = current_app.config.get("GOOGLE_OIDC_API_CLIENT_IDS", []) if primary_client_id: client_ids.add(primary_client_id) if legacy_api_client_id: client_ids.add(legacy_api_client_id) if api_client_ids: client_ids.update(api_client_ids) ALLOWED_CLIENT_IDS = list(client_ids) if not ALLOWED_CLIENT_IDS: return abort( HTTP_STATUS_CODE_BAD_REQUEST, "No OIDC client IDs defined in the configuration file.", ) # Authenticating session, see more details here: # https://www.oauth.com/oauth2-servers/signing-in-with-google/\ # verifying-the-user-info/ # Sending a request to Google to verify that the access token # is valid, to be able to validate the session. data = {"access_token": token} bearer_token_response = requests.post(TOKEN_URI, data=data) if bearer_token_response.status_code != HTTP_STATUS_CODE_OK: return abort(HTTP_STATUS_CODE_BAD_REQUEST, "Unable to validate access token.") bearer_token_json = bearer_token_response.json() data = {"id_token": id_token} token_response = requests.post(TOKEN_URI, data=data) token_json = token_response.json() verified = token_json.get("email_verified", False) if not verified: return abort( HTTP_STATUS_CODE_UNAUTHORIZED, "Session not authenticated or account not verified", ) if bearer_token_json.get("azp", "a") != token_json.get("azp", "x"): return abort( HTTP_STATUS_CODE_UNAUTHORIZED, "Auth token and client tokens don't match, azp differs.", ) if bearer_token_json.get("email", "a") != token_json.get("email", "b"): return abort( HTTP_STATUS_CODE_UNAUTHORIZED, "Auth token and client tokens don't match, email differs.", ) try: discovery_document = get_oauth2_discovery_document() except DiscoveryDocumentError as e: return abort( HTTP_STATUS_CODE_BAD_REQUEST, "Unable to discover document, with error: {0!s}".format(e), ) expected_issuer = discovery_document["issuer"] # pylint: disable=broad-except try: validate_jwt(token_json, expected_issuer) except (ImportError, NameError, UnboundLocalError): raise except ( JwtValidationError, JwtKeyError, Exception, ) as e: return abort( HTTP_STATUS_CODE_UNAUTHORIZED, "Unable to validate the JWT token, with error: {0!s}.".format(e), ) read_client_id = token_json.get("aud", "") if read_client_id not in ALLOWED_CLIENT_IDS: return abort( HTTP_STATUS_CODE_UNAUTHORIZED, "Client ID {0:s} does not match server configuration for " "client".format(read_client_id), ) read_scopes = bearer_token_json.get("scope", "").split() if not set(read_scopes) == set(SCOPES): return abort( HTTP_STATUS_CODE_UNAUTHORIZED, "Client scopes differ from what they should be (email, openid, " "profile) = {} VS {}".format(SCOPES, read_scopes), ) validated_email = token_json.get("email") # Check if the authenticating user is part of the allowed domains. hosted_domains = current_app.config.get("GOOGLE_OIDC_HOSTED_DOMAIN") if hosted_domains: _, _, domain = validated_email.partition("@") if domain.lower() != hosted_domains.lower(): return abort( HTTP_STATUS_CODE_UNAUTHORIZED, "Domain {0:s} is not allowed to authenticate against this " "instance.".format(domain), ) allowed_users = current_app.config.get("GOOGLE_OIDC_ALLOWED_USERS") # TODO: Remove that after a 6 months, this following check is to ensure # compatibility of config file if not allowed_users: current_app.logger.warning("Warning, GOOGLE_OIDC_USER_WHITELIST has " "been deprecated. Please update " "timesketch.conf.") allowed_users = current_app.config.get("GOOGLE_OIDC_USER_WHITELIST", []) # Check if the authenticating user is on the allow list. if allowed_users: if validated_email not in allowed_users: return abort(HTTP_STATUS_CODE_UNAUTHORIZED, "Unauthorized request, user not allowed") user = User.get_or_create(username=validated_email, name=validated_email) login_user(user, remember=True) # Log the user in and setup the session. if current_user.is_authenticated: return """ <h1>Authenticated</h1> """ return abort(HTTP_STATUS_CODE_BAD_REQUEST, "User is not authenticated.")
def google_openid_connect(): """Handler for the Google OpenID Connect callback. Reference: https://developers.google.com/identity/protocols/OpenIDConnect Returns: Redirect response. """ error = request.args.get("error", None) if error: current_app.logger.error("OAuth2 flow error: {}".format(error)) return abort(HTTP_STATUS_CODE_BAD_REQUEST, "OAuth2 flow error: {0!s}".format(error)) try: code = request.args["code"] client_csrf_token = request.args.get("state") server_csrf_token = session[CSRF_KEY] except KeyError as e: return abort(HTTP_STATUS_CODE_BAD_REQUEST, "Client CSRF error, no CSRF key stored") if client_csrf_token != server_csrf_token: return abort(HTTP_STATUS_CODE_BAD_REQUEST, "Invalid CSRF token") try: encoded_jwt = get_encoded_jwt_over_https(code) except JwtFetchError as e: return abort(HTTP_STATUS_CODE_BAD_REQUEST, "Jwt Fetch error, {0!s}".format(e)) try: discovery_document = get_oauth2_discovery_document() except DiscoveryDocumentError as e: return abort( HTTP_STATUS_CODE_BAD_REQUEST, "Unable to discover document, with error: {0!s}".format(e), ) algorithm = discovery_document["id_token_signing_alg_values_supported"][0] expected_audience = current_app.config.get("GOOGLE_OIDC_CLIENT_ID") expected_domain = current_app.config.get("GOOGLE_OIDC_HOSTED_DOMAIN") expected_issuer = discovery_document["issuer"] # Fetch the public key and try to validate the JWT. try: public_key = get_public_key_for_jwt(encoded_jwt, discovery_document["jwks_uri"]) decoded_jwt = decode_jwt(encoded_jwt, public_key, algorithm, expected_audience) validate_jwt(decoded_jwt, expected_issuer, expected_domain) except (JwtValidationError, JwtKeyError) as e: current_app.logger.error("{}".format(e)) return abort( HTTP_STATUS_CODE_UNAUTHORIZED, "Unable to validate request, with error: {0!s}".format(e), ) validated_email = decoded_jwt.get("email") allowed_users = current_app.config.get("GOOGLE_OIDC_ALLOWED_USERS") # Check if the authenticating user is allowed. if allowed_users: if validated_email not in allowed_users: return abort(HTTP_STATUS_CODE_UNAUTHORIZED, "Unauthorized request, user not allowed") user = User.get_or_create(username=validated_email, name=validated_email) login_user(user) # Log the user in and setup the session. if current_user.is_authenticated: return redirect(request.args.get("next") or "/") return abort(HTTP_STATUS_CODE_BAD_REQUEST, "User is not authenticated.")
def login(): """Handler for the login page view. There are two ways of authentication. 1) If Single Sign On (SSO) is enabled in configuration and the environment variable is present, e.g. REMOTE_USER then the system will get or create the user object and setup a session for the user. 2) Local authentication is used if SSO login is not enabled. This will authenticate the user against the local user database. Returns: Redirect if authentication is successful or template with context otherwise. """ form = UsernamePasswordForm() # SSO login based on environment variable, e.g. REMOTE_USER. if current_app.config.get(u'SSO_ENABLED', False): remote_user_env = current_app.config.get( u'SSO_USER_ENV_VARIABLE', u'REMOTE_USER') sso_group_env = current_app.config.get( u'SSO_GROUP_ENV_VARIABLE', None) remote_user = request.environ.get(remote_user_env, None) if remote_user: user = User.get_or_create(username=remote_user, name=remote_user) login_user(user) # If we get groups from the SSO system create the group(s) in # Timesketch and add/remove the user from it. if sso_group_env: groups_string = request.environ.get(sso_group_env, u'') separator = current_app.config.get( u'SSO_GROUP_SEPARATOR', u';') not_member_sign = current_app.config.get( u'SSO_GROUP_NOT_MEMBER_SIGN', None) for group_name in groups_string.split(separator): remove_group = False if not_member_sign: remove_group = group_name.startswith(not_member_sign) group_name = group_name.lstrip(not_member_sign) # Get or create the group in the Timesketch database. group = Group.get_or_create(name=group_name) if remove_group: if group in user.groups: user.groups.remove(group) else: if group not in user.groups: user.groups.append(group) # Commit the changes to the database. db_session.commit() # Login form POST if form.validate_on_submit: user = User.query.filter_by(username=form.username.data).first() if user: if user.check_password(plaintext=form.password.data): login_user(user) if current_user.is_authenticated: return redirect(request.args.get(u'next') or u'/') return render_template(u'user/login.html', form=form)