Esempio n. 1
0
 def _check_login_to_app_allowed(self, ctx):
     # type: (LoginCtx) -> bool
     if ctx.input['current_app'] not in self.sso_conf.apps.login_allowed:
         if self.sso_conf.apps.inform_if_app_invalid:
             raise ValidationError(status_code.app_list.invalid, True)
         else:
             raise ValidationError(status_code.auth.not_allowed, True)
     else:
         return True
Esempio n. 2
0
    def _validate_app_list(self, session, sso_conf, current_app, app_list):
        """ Raises ValidationError if input app_list is invalid, e.g. includes an unknown one.
        """
        # All of input apps must have been already defined in configuration
        for app in app_list:
            if app not in sso_conf.apps.all:
                raise ValidationError(status_code.app_list.invalid, sso_conf.signup.inform_if_app_invalid)

        # Current app, the one the user is signed up through, must allow user signup
        if current_app not in sso_conf.apps.signup_allowed:
            raise ValidationError(status_code.app_list.no_signup, sso_conf.signup.inform_if_app_invalid)
Esempio n. 3
0
    def _validate_username(self, session, sso_conf, username):
        """ Raises ValidationError if username is invalid, e.g. is not too long.
        """
        # Username must not be too long
        if len(username) > sso_conf.signup.max_length_username:
            raise ValidationError(status_code.username.too_long, sso_conf.signup.inform_if_user_invalid)

        # Username must not contain whitespace
        if self._has_whitespace(username):
            raise ValidationError(status_code.username.has_whitespace, sso_conf.signup.inform_if_user_invalid)

        # Username must not contain restricted keywords
        for elem in sso_conf.user_validation.reject_username:
            if elem in username:
                raise ValidationError(status_code.username.invalid, sso_conf.signup.inform_if_user_invalid)
Esempio n. 4
0
    def create(self,
               name,
               value,
               expiration=None,
               encrypt=False,
               user_id=None):
        """ Creates a new named attribute, raising an exception if it already exists.
        """
        # Audit comes first
        audit_pii.info(self.cid,
                       'attr.create',
                       self.current_user_id,
                       user_id,
                       extra={
                           'current_app': self.current_app,
                           'remote_addr': self.remote_addr
                       })

        # Check access permissions to that user's attributes
        self._require_correct_user('create', user_id)

        with closing(self.odb_session_func()) as session:
            try:
                return self._create(session, name, value, expiration, encrypt,
                                    user_id)
            except IntegrityError:
                logger.warn(format_exc())
                raise ValidationError(status_code.attr.already_exists)
Esempio n. 5
0
    def _require_correct_user(self, op, target_user_id):
        """ Makes sure that during current operation self.current_user_id is the same as target_user_id (which means that
        a person accesses his or her own attribute) or that current operation is performed by a super-user.
        """
        if self.is_super_user or self.current_user_id == target_user_id:
            result = status_code.ok
            log_func = audit_pii.info
        else:
            result = status_code.error
            log_func = audit_pii.warn

        log_func(self.cid,
                 '_require_correct_user',
                 self.current_user_id,
                 target_user_id,
                 result,
                 extra={
                     'current_app': self.current_app,
                     'remote_addr': self.remote_addr,
                     'op': op,
                     'is_super_user': self.is_super_user
                 })

        if result != status_code.ok:
            raise ValidationError(status_code.auth.not_allowed)
Esempio n. 6
0
    def get_list(self, cid, ust, target_ust, current_ust, current_app, remote_addr, _unused_user_agent=None):
        """ Returns a list of sessions. Regular users may receive basic information about their own sessions only
        whereas super-users may look up any other user's session list.
        """
        # PII audit comes first
        audit_pii.info(cid, 'session.get_list', extra={'current_app':current_app, 'remote_addr':remote_addr})

        # Local aliases
        has_ust = bool(ust)
        current_ust_elem = ust if has_ust else current_ust

        current_session = self.get_current_session(cid, current_ust_elem, current_app, remote_addr, False)

        # We return a list of sessions for currently logged in user
        if has_ust:
            return self._get_session_list_by_user_id(current_session.user_id)

        else:
            # If we are to return a list of sessions for another UST, we need to be a super-user
            if not current_session.is_super_user:
                logger.warn(
                    'Current UST does not belong to a super-user, cannot continue (session.get_list), current user is ' \
                    '`%s` `%s`', current_session.user_id, current_session.username)
                raise ValidationError(status_code.auth.not_allowed, True)

            # If we are here, it means that we are a super-user and we are to return sessions
            # by another person's UST but there is still a chance that this other person is actually us.
            if current_ust == target_ust:
                return self._get_session_list_by_user_id(current_session.user_id)
            else:
                return self._get_session_list_by_ust(target_ust)
Esempio n. 7
0
def check_remote_app_exists(current_app, apps_all, logger):
    if current_app not in apps_all:
        logger.warn('Invalid current_app `%s`, not among `%s', current_app,
                    apps_all)
        raise ValidationError(status_code.app_list.invalid)
    else:
        return True
Esempio n. 8
0
 def _check_is_approved(self, user):
     if not user.approval_status == const.approval_status.approved:
         if self.sso_conf.login.inform_if_not_approved:
             raise ValidationError(status_code.auth.invalid_signup_status,
                                   True)
     else:
         return True
Esempio n. 9
0
 def _check_signup_status(self, user):
     if user.sign_up_status != const.signup_status.final:
         if self.sso_conf.login.inform_if_not_confirmed:
             raise ValidationError(status_code.auth.invalid_signup_status,
                                   True)
     else:
         return True
Esempio n. 10
0
    def _check_login_metadata_allowed(self, ctx):
        if ctx.has_remote_addr or ctx.has_user_agent:
            if ctx.input[
                    'current_app'] not in self.sso_conf.apps.login_metadata_allowed:
                raise ValidationError(status_code.password.must_send_new,
                                      False)

        return True
Esempio n. 11
0
    def _check_login_metadata_allowed(self, ctx):
        # type: (LoginCtx) -> bool

        if ctx.has_remote_addr or ctx.has_user_agent:
            if ctx.input['current_app'] not in self.sso_conf.apps.login_metadata_allowed:
                raise ValidationError(status_code.metadata.not_allowed, False)

        return True
Esempio n. 12
0
    def _check_password_expired(self, user, _now=datetime.utcnow):
        # type: (SSOUser, datetime) -> bool

        if _now() > user.password_expiry:
            if self.sso_conf.password.inform_if_expired:
                raise ValidationError(status_code.password.expired, True)
        else:
            return True
Esempio n. 13
0
    def _check_must_send_new_password(self, ctx, user):
        # type: (LoginCtx, SSOUser) -> bool

        if user.password_must_change and not ctx.input.get('new_password'):
            if self.sso_conf.password.inform_if_must_be_changed:
                raise ValidationError(status_code.password.must_send_new, True)
        else:
            return True
Esempio n. 14
0
    def _check_user_not_locked(self, user):
        # type: (SSOUser) -> bool

        if user.is_locked:
            if self.sso_conf.login.inform_if_locked:
                raise ValidationError(status_code.auth.locked, True)
        else:
            return True
Esempio n. 15
0
    def _run_user_checks(self, ctx, user, check_if_password_expired=True):
        """ Runs a series of checks for incoming request and user.
        """
        # Input application must have been previously defined
        if not self._check_remote_app_exists(ctx):
            raise ValidationError(status_code.auth.not_allowed, True)

        # If applicable, requests must originate in a white-listed IP address
        if not self._check_remote_ip_allowed(ctx, user):
            raise ValidationError(status_code.auth.not_allowed, True)

        # User must not have been locked out of the auth system
        if not self._check_user_not_locked(user):
            raise ValidationError(status_code.auth.not_allowed, True)

        # If applicable, user must be fully signed up, including account creation's confirmation
        if not self._check_signup_status(user):
            raise ValidationError(status_code.auth.not_allowed, True)

        # If applicable, user must be approved by a super-user
        if not self._check_is_approved(user):
            raise ValidationError(status_code.auth.not_allowed, True)

        # Password must not have expired, but only if input flag tells us to,
        # it may be possible that a user's password has already expired
        # and that person wants to change it in this very call, in which case
        # we cannot reject it on the basis that it is expired - no one would be able
        # to change expired passwords then.
        if check_if_password_expired:
            if not self._check_password_expired(user):
                raise ValidationError(status_code.auth.not_allowed, True)

        # Current application must be allowed to send login metadata
        if not self._check_login_metadata_allowed(ctx):
            raise ValidationError(status_code.auth.not_allowed, True)
Esempio n. 16
0
    def _validate_password(self, session, sso_conf, password):
        """ Raises ValidationError if password is invalid, e.g. it is too simple.
        """
        # Password may not be too short
        if len(password) < sso_conf.password.min_length:
            raise ValidationError(status_code.password.too_short, sso_conf.password.inform_if_invalid)

        # Password may not be too long
        if len(password) > sso_conf.password.max_length:
            raise ValidationError(status_code.password.too_long, sso_conf.password.inform_if_invalid)

        # Password's default complexity is checked case-insensitively
        password = password.lower()

        # Password may not contain most commonly used ones
        for elem in sso_conf.password.reject_list:
            if elem in password:
                raise ValidationError(status_code.password.invalid, sso_conf.password.inform_if_invalid)
Esempio n. 17
0
    def _handle_sso_POST(self, ctx):
        """ Verifies whether an input session exists or not.
        """
        # Make sure target UST actually was given on input
        if ctx.input.target_ust == _invalid:
            raise ValidationError(status_code.session.no_such_session)

        self.response.payload.is_valid = self.sso.user.session.verify(self.cid, ctx.input.target_ust, ctx.input.current_ust,
            ctx.input.current_app, ctx.remote_addr)
Esempio n. 18
0
    def _validate_email(self, session, sso_conf, email):
        """ Raises ValidationError if email is invalid, e.g. already exists.
        """
        # E-mail may be required
        if sso_conf.signup.is_email_required and not email:
            raise ValidationError(status_code.email.missing, sso_conf.signup.inform_if_email_invalid)

        # E-mail must not be too long
        if len(email) > sso_conf.signup.max_length_email:
            raise ValidationError(status_code.email.too_long, sso_conf.signup.inform_if_email_invalid)

        # E-mail must not contain whitespace
        if self._has_whitespace(email):
            raise ValidationError(status_code.email.has_whitespace, sso_conf.signup.inform_if_email_invalid)

        # E-mail must not contain restricted keywords
        for elem in sso_conf.user_validation.reject_email:
            if elem in email:
                raise ValidationError(status_code.email.invalid, sso_conf.signup.inform_if_email_invalid)
Esempio n. 19
0
    def get_current_session(self, cid, current_ust, current_app, remote_addr, needs_super_user):
        """ Returns current session info or raises an exception if it could not be found.
        Optionally, requires that a super-user be owner of current_ust.
        """
        # PII audit comes first
        audit_pii.info(cid, 'session.get_current_session', extra={'current_app':current_app, 'remote_addr':remote_addr})

        # Verify current session's very existence first ..
        current_session = self._get_session(current_ust, current_app, remote_addr, 'get_current_session')
        if not current_session:
            logger.warn('Could not verify session `%s` `%s` `%s` `%s`',
                current_ust, current_app, remote_addr, format_exc())
            raise ValidationError(status_code.auth.not_allowed, True)

        # .. the session exists but it may be still the case that we require a super-user on input.
        if needs_super_user:
            if not current_session.is_super_user:
                logger.warn(
                    'Current UST does not belong to a super-user, cannot continue (session.get_current_session), ' \
                    'current user is `%s` `%s`', current_session.user_id, current_session.username)
                raise ValidationError(status_code.auth.not_allowed, True)

        return current_session
Esempio n. 20
0
    def _get(self, session, ust, current_app, remote_addr, ctx_source, needs_decrypt=True, renew=False, needs_attrs=False,
        user_agent=None, check_if_password_expired=True, _now=datetime.utcnow, _opaque=GENERIC.ATTR_NAME, skip_sec=False):
        """ Verifies if input user session token is valid and if the user is allowed to access current_app.
        On success, if renew is True, renews the session. Returns all session attributes or True,
        depending on needs_attrs's value.
        """
        # type: (object, unicode, unicode, bool, bool, bool, bool, datetime, unicode) -> object

        now = _now()
        ctx = VerifyCtx(self.decrypt_func(ust) if needs_decrypt else ust, remote_addr, current_app)

        # Look up user and raise exception if not found by input UST
        sso_info = self._get_session_by_ust(session, ctx.ust, now)

        # Invalid UST or the session has already expired but in either case
        # we can not access it.
        if not sso_info:
            raise ValidationError(status_code.session.no_such_session, False)

        if skip_sec:
            return sso_info if needs_attrs else True
        else:

            # Common auth checks
            self._run_user_checks(ctx, sso_info, check_if_password_expired)

            # Everything is validated, we can renew the session, if told to.
            if renew:

                # Update current interaction details for this session
                opaque = getattr(sso_info, _opaque) or {}
                session_state_change_list = self._extract_session_state_change_list(sso_info)
                self.update_session_state_change_list(session_state_change_list, remote_addr, user_agent, ctx_source, now)
                opaque['session_state_change_list'] = session_state_change_list

                # Set a new expiration time
                expiration_time = now + timedelta(minutes=self.sso_conf.session.expiry)

                session.execute(
                    SessionModelUpdate().values({
                        'expiration_time': expiration_time,
                        GENERIC.ATTR_NAME: dumps(opaque),
                }).where(
                    SessionModelTable.c.ust==ctx.ust
                ))
                return expiration_time
            else:
                # Indicate success
                return sso_info if needs_attrs else True
Esempio n. 21
0
File: attr.py Progetto: danlg/zato
    def delete(self, data, user_id=None, _utcnow=_utcnow):
        """ Deletes one or more names attributes.
        """
        # Audit comes first
        audit_pii.info(self.cid,
                       'attr.delete/delete_many',
                       self.current_user_id,
                       user_id,
                       extra={
                           'current_app': self.current_app,
                           'remote_addr': self.remote_addr,
                           'data': data,
                           'is_super_user': self.is_super_user
                       })

        # Check access permissions to that user's attributes
        self._require_correct_user('delete', user_id)

        now = _utcnow()
        data = [data] if isinstance(data, basestring) else data

        and_condition = [
            AttrModelTable.c.user_id == (user_id or self.user_id),
            AttrModelTable.c.ust == self.ust,
            AttrModelTable.c.name.in_(data),
            AttrModelTable.c.expiration_time > now,
        ]

        with closing(self.odb_session_func()) as session:

            if self.ust:

                # Check comment in self._update for a comment why the below is needed
                if self.is_sqlite:
                    result = self._ensure_ust_is_not_expired(
                        session, self.ust, now)
                    if not result:
                        raise ValidationError(
                            status_code.session.no_such_session)
                else:
                    and_condition.extend([
                        AttrModelTable.c.ust == SSOSessionTable.c.ust,
                        SSOSessionTable.c.expiration_time > now
                    ])

            session.execute(
                AttrModelTableDelete().\
                where(and_(*and_condition)))
            session.commit()
Esempio n. 22
0
    def _handle_sso_GET(self, ctx):
        """ Returns details of a particular session.
        """
        # Make sure target UST actually was given on input
        if ctx.input.target_ust == _invalid:
            raise ValidationError(status_code.session.no_such_session)

        # Get result
        result = self.sso.user.session.get(
            self.cid, ctx.input.target_ust, ctx.input.current_ust,
            ctx.input.current_app, ctx.remote_addr,
            self.wsgi_environ.get('HTTP_USER_AGENT'))

        # Return output
        self.response.payload = result.to_dict()
Esempio n. 23
0
    def _handle_sso_GET(self, ctx):
        """ Returns details of a particular session.
        """
        # Make sure target UST actually was given on input
        if ctx.input.target_ust == _invalid:
            raise ValidationError(status_code.session.no_such_session)

        # Get result
        result = self.sso.user.session.get(self.cid, ctx.input.target_ust, ctx.input.current_ust,
            ctx.input.current_app, ctx.remote_addr)

        # Serialize datetime objects to string
        result['creation_time'] = result['creation_time'].isoformat()
        result['expiration_time'] = result['expiration_time'].isoformat()

        # Return output
        self.response.payload = result
Esempio n. 24
0
    def _get(self,
             session,
             ust,
             current_app,
             remote_addr,
             needs_decrypt=True,
             renew=False,
             needs_attrs=False,
             check_if_password_expired=True,
             _now=datetime.utcnow):
        """ Verifies if input user session token is valid and if the user is allowed to access current_app.
        On success, if renew is True, renews the session. Returns all session attributes or True,
        depending on needs_attrs's value.
        """
        now = _now()
        ctx = VerifyCtx(
            self.decrypt_func(ust) if needs_decrypt else ust, remote_addr,
            current_app)

        # Look up user and raise exception if not found by input UST
        sso_info = self._get_session_by_ust(session, ctx.ust, now)

        # Invalid UST or the session has already expired but in either case
        # we can not access it.
        if not sso_info:
            raise ValidationError(status_code.session.no_such_session, False)

        # Common auth checks
        self._run_user_checks(ctx, sso_info, check_if_password_expired)

        # Everything is validated, we can renew the session, if told to.
        if renew:
            expiration_time = now + timedelta(
                minutes=self.sso_conf.session.expiry)
            session.execute(SessionModelUpdate().values({
                'expiration_time':
                expiration_time,
            }).where(SessionModelTable.c.ust == ctx.ust))
            return expiration_time
        else:
            # Indicate success
            return sso_info if needs_attrs else True
Esempio n. 25
0
File: attr.py Progetto: danlg/zato
    def _call_many(self,
                   func,
                   data,
                   expiration=None,
                   encrypt=False,
                   user_id=None):
        """ A reusable method for manipulation of multiple attributes at a time.
        """
        # Audit comes first
        audit_pii.info(self.cid,
                       'attr._call_many',
                       self.current_user_id,
                       user_id,
                       extra={
                           'current_app': self.current_app,
                           'remote_addr': self.remote_addr,
                           'is_super_user': self.is_super_user,
                           'func': func.__func__.__name__
                       })

        with closing(self.odb_session_func()) as session:
            for item in data:

                # Check access permissions to that user's attributes
                _user_id = item.get('user_id', user_id)
                self._require_correct_user('_call_many', _user_id)

                func(session,
                     item['name'],
                     item['value'],
                     item.get('expiration', expiration),
                     item.get('encrypt', encrypt),
                     _user_id,
                     needs_commit=False)

            # Commit now everything added to session thus far
            try:
                session.commit()
            except IntegrityError:
                logger.warn(format_exc())
                raise ValidationError(status_code.attr.already_exists)
Esempio n. 26
0
    def _validate_username_email(self, session, sso_conf, username, email, check_email):
        """ Validation common to usernames and emails.
        """
        # Check if user exists either by username or email
        user = user_exists(session, username, email, check_email)

        if user:

            if check_email:
                if user.username == username and user.email == email:
                    sub_status = [status_code.username.exists, status_code.email.exists]
                    return_status = sso_conf.signup.inform_if_user_exists and sso_conf.signup.inform_if_email_exists

            elif user.username == username:
                sub_status = status_code.username.exists
                return_status = sso_conf.signup.inform_if_user_exists

            elif user.email == email:
                sub_status = status_code.email.exists
                return_status = sso_conf.signup.inform_if_email_exists

            raise ValidationError(sub_status, return_status)
Esempio n. 27
0
    def login(self,
              ctx,
              _ok=status_code.ok,
              _now=datetime.utcnow,
              _timedelta=timedelta):
        """ Logs a user in, returning session info on success or raising ValidationError on any error.
        """
        # Look up user and raise exception if not found by username
        with closing(self.odb_session_func()) as session:
            user = get_user_by_username(session, ctx.input['username'])
            if not user:
                raise ValidationError(status_code.auth.not_allowed, False)

            # Check credentials first to make sure that attackers do not learn about any sort
            # of metadata (e.g. is the account locked) if they do not know username and password.
            if not self._check_credentials(ctx, user):
                raise ValidationError(status_code.auth.not_allowed, False)

            # It must be possible to log into the application requested (CRM above)
            self._check_login_to_app_allowed(ctx)

            # Common auth checks
            self._run_user_checks(ctx, user)

            # If applicable, password may not be about to expire (this must be after checking that it has not already).
            # Note that it may return a specific status to return (warning or error)
            _about_status = self._check_password_about_to_expire(user)
            if _about_status is not True:
                if _about_status == status_code.warning:
                    _status_code = status_code.password.w_about_to_exp
                    inform = True
                else:
                    _status_code = status_code.password.e_about_to_exp
                    inform = False

                raise ValidationError(_status_code, inform, _about_status)

            # If password is marked as as requiring a change upon next login but a new one was not sent, reject the request.
            self._check_must_send_new_password(ctx, user)

            # If new password is required, we need to validate and save it before session can be created.
            # Note that at this point we already know that the old password was correct so it is safe to set the new one
            # if it is confirmed to be valid. We also know that there is some new password on input because otherwise
            # the check above would have raised a ValidationError.
            if user.password_must_change:
                try:
                    validate_password(self.sso_conf, ctx.input['new_password'])
                except ValidationError as e:
                    if e.return_status:
                        raise ValidationError(e.sub_status, e.return_status,
                                              e.status)
                else:
                    set_password(self.odb_session_func, self.encrypt_func,
                                 self.hash_func, self.sso_conf, user.user_id,
                                 ctx.input['new_password'], False)

            # All validated, we can create a session object now
            creation_time = _now()
            expiration_time = creation_time + timedelta(
                minutes=self.sso_conf.session.expiry)
            ust = new_user_session_token()

            session.execute(SessionModelInsert().values({
                'ust':
                ust,
                'creation_time':
                creation_time,
                'expiration_time':
                expiration_time,
                'user_id':
                user.id,
                'remote_addr':
                ', '.join(str(elem) for elem in ctx.remote_addr),
                'user_agent':
                ctx.user_agent,
            }))
            session.commit()

            info = SessionInfo()
            info.username = user.username
            info.user_id = user.user_id
            info.ust = self.encrypt_func(ust.encode('utf8'))
            info.creation_time = creation_time
            info.expiration_time = expiration_time

            return info
Esempio n. 28
0
    def login(self,
              ctx,
              _ok=status_code.ok,
              _now=datetime.utcnow,
              _timedelta=timedelta,
              _dummy_password=uuid4().hex,
              is_logged_in_ext=True):
        """ Logs a user in, returning session info on success or raising ValidationError on any error.
        """
        # type: (LoginCtx, unicode, datetime, timedelta, unicode, bool) -> SessionInfo

        # Look up user and raise exception if not found by username
        with closing(self.odb_session_func()) as session:

            if ctx.input.get('username'):
                user = get_user_by_username(
                    session, ctx.input['username'])  # type: SSOUser
            else:
                user = get_user_by_id(session,
                                      ctx.input['user_id'])  # type: SSOUser

            # If the user is already logged in externally, this flag will be True,
            # in which case we do not check the credentials - we already know they are valid
            # because they were checked externally and user_id is the SSO user linked to the
            # already validated external credentials.
            if not is_logged_in_ext:

                # Check credentials first to make sure that attackers do not learn about any sort
                # of metadata (e.g. is the account locked) if they do not know username and password.
                if not self._check_credentials(
                        ctx, user.password if user else _dummy_password):
                    raise ValidationError(status_code.auth.not_allowed, False)

                # Check input TOTP key if two-factor authentication is enabled
                if user.is_totp_enabled:
                    input_totp_code = ctx.input.get('totp_code')
                    if not input_totp_code:
                        logger.warn('Missing TOTP code; user `%s`',
                                    user.username)
                        raise ValidationError(status_code.auth.not_allowed,
                                              False)
                    else:
                        user_totp_key = self.decrypt_func(user.totp_key)
                        if not CryptoManager.verify_totp_code(
                                user_totp_key, input_totp_code):
                            logger.warn('Invalid TOTP code; user `%s`',
                                        user.username)
                            raise ValidationError(status_code.auth.not_allowed,
                                                  False)

            # It must be possible to log into the application requested (CRM above)
            self._check_login_to_app_allowed(ctx)

            # Common auth checks
            self._run_user_checks(ctx, user)

            # We assume that we will not have to warn about an approaching password expiry
            has_w_about_to_exp = False

            # If applicable, password may be about to expire (this must be after checking that it has not already).
            # Note that it may return a specific status to return (warning or error)
            _about_status = self._check_password_about_to_expire(user)
            if _about_status is not True:
                if _about_status == status_code.warning:
                    has_w_about_to_exp = True
                else:
                    raise ValidationError(status_code.password.e_about_to_exp,
                                          False, _about_status)

            # If password is marked as requiring a change upon next login but a new one was not sent, reject the request.
            self._check_must_send_new_password(ctx, user)

            # If new password is required, we need to validate and save it before session can be created.
            # Note that at this point we already know that the old password was correct so it is safe to set the new one
            # if it is confirmed to be valid. We also know that there is some new password on input because otherwise
            # the check above would have raised a ValidationError.
            if user.password_must_change:
                try:
                    validate_password(self.sso_conf,
                                      ctx.input.get('new_password'))
                except ValidationError as e:
                    if e.return_status:
                        raise ValidationError(e.sub_status, e.return_status,
                                              e.status)
                else:
                    set_password(self.odb_session_func, self.encrypt_func,
                                 self.hash_func, self.sso_conf, user.user_id,
                                 ctx.input['new_password'], False)

            # All validated, we can create a session object now
            creation_time = _now()
            expiration_time = creation_time + timedelta(
                minutes=self.sso_conf.session.expiry)
            ust = new_user_session_token()

            # Create current interaction details for this session
            session_state_change_list = []
            self.update_session_state_change_list(session_state_change_list,
                                                  ctx.remote_addr,
                                                  ctx.user_agent, 'login',
                                                  creation_time)
            opaque = {'session_state_change_list': session_state_change_list}

            session.execute(SessionModelInsert().values({
                'ust':
                ust,
                'creation_time':
                creation_time,
                'expiration_time':
                expiration_time,
                'user_id':
                user.id,
                'auth_type':
                const.auth_type.default,
                'auth_principal':
                user.username,
                'remote_addr':
                ', '.join(str(elem) for elem in ctx.remote_addr),
                'user_agent':
                ctx.user_agent,
                'ext_session_id':
                ctx.ext_session_id,
                GENERIC.ATTR_NAME:
                dumps(opaque)
            }))
            session.commit()

            info = SessionInfo()
            info.username = user.username
            info.user_id = user.user_id
            info.ust = self.encrypt_func(ust.encode('utf8'))
            info.creation_time = creation_time
            info.expiration_time = expiration_time
            info.has_w_about_to_exp = has_w_about_to_exp

            return info
Esempio n. 29
0
File: attr.py Progetto: danlg/zato
    def _update(self,
                session,
                name,
                value=None,
                expiration=None,
                encrypt=False,
                user_id=None,
                needs_commit=True,
                _utcnow=_utcnow):
        """ A low-level implementation of self.update which expects an SQL session on input.
        """
        # Audit comes first
        audit_pii.info(self.cid,
                       'attr._update',
                       self.current_user_id,
                       user_id,
                       extra={
                           'current_app': self.current_app,
                           'remote_addr': self.remote_addr,
                           'name': name,
                           'expiration': expiration,
                           'encrypt': encrypt,
                           'is_super_user': self.is_super_user
                       })

        # Check access permissions to that user's attributes
        self._require_correct_user('_update', user_id)

        now = _utcnow()
        values = {'last_modified': now}

        if value:
            values['value'] = dumps(
                self.encrypt_func(value.encode('utf8')) if encrypt else value)

        if expiration:
            values['expiration_time'] = now + timedelta(seconds=expiration)

        and_condition = [
            AttrModelTable.c.user_id == (user_id or self.user_id),
            AttrModelTable.c.ust == self.ust,
            AttrModelTable.c.name == name,
            AttrModelTable.c.expiration_time > now,
        ]

        if self.ust:

            # SQLite needs to be treated in a special way, otherwise we get an exception from SQLAlchemy
            # NotImplementedError: This backend does not support multiple-table criteria within UPDATE
            # which means that on SQLite we need an additional query.
            if self.is_sqlite:
                result = self._ensure_ust_is_not_expired(
                    session, self.ust, now)
                if not result:
                    raise ValidationError(status_code.session.no_such_session)
            else:
                and_condition.extend([
                    AttrModelTable.c.ust == SSOSessionTable.c.ust,
                    SSOSessionTable.c.expiration_time > now
                ])

        session.execute(
            AttrModelTableUpdate().\
            values(values).\
            where(and_(*and_condition)))

        if needs_commit:
            session.commit()