Exemplo n.º 1
0
    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
Exemplo n.º 2
0
    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
Exemplo n.º 3
0
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
Exemplo n.º 4
0
 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))
Exemplo n.º 5
0
 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))
Exemplo n.º 6
0
 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))
Exemplo n.º 7
0
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")
Exemplo n.º 8
0
    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')
Exemplo n.º 9
0
    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")
Exemplo n.º 10
0
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)
Exemplo n.º 11
0
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)
Exemplo n.º 12
0
    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()
Exemplo n.º 13
0
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)
Exemplo n.º 14
0
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.')
Exemplo n.º 15
0
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.')
Exemplo n.º 16
0
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)
Exemplo n.º 17
0
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)
Exemplo n.º 18
0
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)
Exemplo n.º 19
0
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
Exemplo n.º 20
0
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.")
Exemplo n.º 21
0
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.")
Exemplo n.º 22
0
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)