Ejemplo n.º 1
0
 def __enter__(self):
     try:
         res, self.context = kerberos.authGSSServerInit(self.serviceType)
     except kerberos.KrbError as e:
         msg = repr(e)
         log.msg(msg)
         raise LoginFailed(msg)
     
     if res < 0:
         raise LoginFailed()
     
     return self
Ejemplo n.º 2
0
    def decode(self, response, request):
        """Parse the Authorization header and also extract the OAuth token.

        The authorization response must contain a base64-encoded,
        colon-separated username and password. The response parsing code
        below is from C{twisted.web._auth.basic.BasicCredentialFactory} (as
        opposed to using the more modern base64 module). We also look for
        the C{X-FluidDB-Access-Token} header.  OAuth2 requests must be made
        over a secure transport.

        @param response: The value of the HTTP Authorization header. If
            this is empty this is an anonymous 'anon' user request.
        @param request: The incoming HTTP request.
        @raise LoginFailed: If the Authorization header is not valid
            base64, or if the Authorization header cannot be split into 2
            pieces by ':', or if the request is not via https, or if the
            C{X-FluidDB-Access-Token} header is not present or empty.
        @return: an L{OAuth2Credentials} instance.
        """
        # Make sure the request is via HTTPS. We can only tell that by
        # looking for the header NGinx sets for us when it processes HTTPS
        # requests.

        if not self.development:
            secureHeader = request.getHeader('X-Forwarded-Protocol')
            if secureHeader != 'https':
                raise LoginFailed('OAuth2 requests must use https.')

        if response:
            try:
                credentials = binascii.a2b_base64(response + '===')
            except binascii.Error:
                raise LoginFailed('Non-base64 credentials.')

            try:
                username, password = credentials.split(':', 1)
            except ValueError:
                raise LoginFailed('Credentials could not be split on colon.')

            try:
                username = username.decode('utf-8')
                password = password.decode('utf-8')
            except UnicodeDecodeError:
                raise LoginFailed('Invalid UTF-8 in credentials.')
        else:
            username = u'anon'
            password = None

        token = request.getAllHeaders().get('x-fluiddb-access-token')
        if not token:
            raise LoginFailed('X-FluidDB-Access-Token header missing/empty.')

        return OAuth2Credentials(username, password, token)
Ejemplo n.º 3
0
 def __exit__(self, exc_type, exc_value, traceback):
     if self.context:
         try:
             kerberos.authGSSServerClean(self.context)
         except kerberos.KrbError as e:
             msg = repr(e)
             log.msg(msg)
             raise LoginFailed(msg)
     
     if exc_value:
         msg = repr(exc_value)
         log.msg(msg)
         raise LoginFailed(msg)
Ejemplo n.º 4
0
    def _makeLdapBase(self, ldapFolders, userName, propertyName):
        try:
            ldapBases = []
            for folder in ldapFolders.split(','):
                folder = folder.strip()
                if not folder:
                    continue

                parts = []
                for part in folder.split('/'):
                    part = part.strip()
                    if not part:
                        continue
                    parts.append('%s=%s' % (propertyName, part))

                ldapBases.append(','.join(reversed(parts)))

            return ldapBases

        except Exception as e:
            logger.error(
                "Login failed for %s, failed to parse LDAP %s Folders setting",  propertyName,
                userName)

            logger.exception(e)

            raise LoginFailed(
                "An internal error occurred, ask admin to check Attune logs")
Ejemplo n.º 5
0
    def authenticate(self, req: Request) -> Deferred:
        # To avoid cross-site request forgery, we must authenticate every API
        # call and not use session cookies. Since API calls are not made using
        # web browsers, most likely the client is not using session cookies
        # anyway.
        #   http://en.wikipedia.org/wiki/Cross-site_request_forgery
        try:
            credentials = req.getCredentials()
        except UnicodeDecodeError as ex:
            return fail(LoginFailed(ex))

        if credentials.name:
            return ensureDeferred(authenticateUser(self.userDB, credentials))
        elif self.project.anonguest:
            return succeed(AnonGuestUser())
        else:
            return fail(LoginFailed())
Ejemplo n.º 6
0
 def authenticate(self, req: Request) -> Deferred:
     user = req.loggedInUser()
     if user is not None:
         # User has already authenticated.
         return succeed(user)
     elif self.project.anonguest:
         return succeed(AnonGuestUser())
     else:
         # User must log in.
         return fail(LoginFailed())
Ejemplo n.º 7
0
    def decode(self, response, request):
        """
        Parse the Authorization header to retrieve the OAuth components.

        @param response: The value of the HTTP Authorization header.
        @param request: The incoming HTTP request.

        @return: an L{OAuthCredentials}.
        @raise L{LoginFailed}: if the format of the Authorization header
            is not valid.
        """
        creds = response.split(',')
        # There are seven required OAuth headers, only version is optional
        if len(creds) >= 7:
            parameters = {}
            for cred in creds:
                parameter, value = cred.split('=')
                parameter = parameter.strip(' \r\n"')
                value = value.strip(' \r\n"')

                key = OAuthCredentials.oauthMapping.get(parameter)
                if key is None:
                    raise LoginFailed('Invalid credentials')
                parameters[key] = unquote(value)

            # Apart from the OAuth header, we also need the URL and the method
            # of the incoming request
            parameters['url'] = request.uri
            parameters['method'] = request.method
            parameters['headers'] = request.getAllHeaders()
            parameters['version'] = parameters.get('version')

            arguments = {}
            for key, value in request.args.iteritems():
                arguments[key.lower()] = value[-1]
            parameters['arguments'] = arguments

            # Check that all parameters are present, otherwise raise an
            # exception
            if len(parameters) == 12:
                return OAuthCredentials(**parameters)
        raise LoginFailed('Invalid credentials')
Ejemplo n.º 8
0
    def checkPassBlocking(self, dbSession, userName, password,
                          forService: int) -> List[str]:

        results = dbSession.query(InternalUserPassword) \
            .join(InternalUserTuple) \
            .filter(InternalUserTuple.userName == userName) \
            .all()

        if not results or not results[0].password:
            raise UserPasswordNotSetException(userName)

        passObj = results[0]
        if passObj.password != PasswordUpdateController.hashPass(password):
            raise LoginFailed("Username or password is incorrect")

        groups = dbSession.query(InternalGroupTuple) \
            .join(InternalUserGroupTuple) \
            .filter(InternalUserGroupTuple.userId == passObj.userId) \
            .all()

        groupNames = [g.groupName for g in groups]

        if forService == self.FOR_ADMIN:
            adminGroup = globalSetting(dbSession, ADMIN_LOGIN_GROUP)
            if adminGroup not in set(groupNames):
                raise LoginFailed("User is not apart of an authorised group")

        elif forService == self.FOR_OFFICE:
            officeGroup = globalSetting(dbSession, OFFICE_LOGIN_GROUP)
            if officeGroup not in set(groupNames):
                raise LoginFailed("User is not apart of an authorised group")

        elif forService == self.FOR_FIELD:
            fieldGroup = globalSetting(dbSession, MOBILE_LOGIN_GROUP)
            if fieldGroup not in set(groupNames):
                raise LoginFailed("User is not apart of an authorised group")

        else:
            raise Exception("InternalAuth:Unhandled forService type %s" %
                            forService)

        return groupNames
Ejemplo n.º 9
0
    def checkPassBlocking(self, dbSession, userName, password,
                          forService: int) -> List[str]:
        """ Login User

        :param forService:
        :param dbSession:
        :param userName: The username of the user.
        :param password: The users secret password.
        :rtype
        """

        assert forService in (1, 2, 3), "Unhandled for service type"

        ldapSettings: List[LdapSetting] = dbSession.query(LdapSetting) \
            .all()

        if not ldapSettings:
            raise Exception("No LDAP servers configured.")

        firstException = None

        for ldapSetting in ldapSettings:
            if forService == self.FOR_ADMIN:
                if not ldapSetting.adminEnabled:
                    continue

            elif forService == self.FOR_OFFICE:
                if not ldapSetting.desktopEnabled:
                    continue

            elif forService == self.FOR_FIELD:
                if not ldapSetting.mobileEnabled:
                    continue

            else:
                raise Exception("InternalAuth:Unhandled forService type %s" % forService)

            try:
                return self._tryLdap(dbSession, userName, password, ldapSetting)
            except LoginFailed as e:
                if not firstException:
                    firstException = e

        logger.error("Login failed for %s, %s",
                     userName, str(firstException))

        if firstException:
            raise firstException

        raise LoginFailed("No LDAP providers found for this service")
Ejemplo n.º 10
0
    def authenticate(self, req: Request) -> Deferred:
        try:
            credentials = req.getCredentials()
        except UnicodeDecodeError as ex:
            return fail(LoginFailed(ex))

        tokenId = credentials.name
        if tokenId:
            try:
                token = authenticateToken(self.tokenDB, credentials)
            except KeyError:
                return fail(LoginFailed(f'Token {tokenId} does not exist'))
            if token.role is not self.__role:
                return fail(
                    Unauthorized(
                        f'Token {tokenId} is of the wrong type for this operation'
                    ))
            if token.expired:
                return fail(Unauthorized(f'Token {tokenId} has expired'))
            return succeed(TokenUser(token))
        elif self.project.anonguest:
            return succeed(AnonGuestUser())
        else:
            return fail(LoginFailed())
Ejemplo n.º 11
0
 def decode(self, challenge, request):
     with ServerGSSContext(self.serviceType) as context:
         res = context.step(challenge)
         if res < 0:
             raise LoginFailed()
         
         response = context.response()
         request.responseHeaders.addRawHeader(
             'www-authenticate',
             '%s %s' % (self.scheme, response)
         )
         
         if res == kerberos.AUTH_GSS_COMPLETE:
             principal = context.userName()
             return NegotiateCredentials(principal)
     
     raise LoginError()
Ejemplo n.º 12
0
    def _checkPassBlocking(self, ormSession, userName, password,
                           isFieldService: bool) -> List[str]:
        if not password:
            raise LoginFailed("Password is empty")

        # TODO Make the client tell us if it's for office or field

        lastException = None

        forService = InternalAuth.FOR_OFFICE
        if isFieldService:
            forService = InternalAuth.FOR_FIELD

        # TRY INTERNAL IF ITS ENABLED
        try:
            if forService == InternalAuth.FOR_FIELD \
                    and globalSetting(ormSession, INTERNAL_AUTH_ENABLED_FOR_FIELD):
                return InternalAuth().checkPassBlocking(
                    ormSession, userName, password, forService)

            if forService == InternalAuth.FOR_OFFICE \
                    and globalSetting(ormSession, INTERNAL_AUTH_ENABLED_FOR_OFFICE):
                return InternalAuth().checkPassBlocking(
                    ormSession, userName, password, forService)

        except Exception as e:
            lastException = e

        # TRY LDAP IF ITS ENABLED
        try:
            if globalSetting(ormSession, LDAP_AUTH_ENABLED):
                return LdapAuth().checkPassBlocking(ormSession, userName,
                                                    password, forService)

        except Exception as e:
            lastException = e

        if lastException:
            raise lastException

        raise Exception(
            "No authentication handlers are enabled, enable one in settings")
Ejemplo n.º 13
0
    def _tryLdap(self, dbSession, userName, password,
                 ldapSetting: LdapSetting) -> List[str]:
        try:

            conn = ldap.initialize(ldapSetting.ldapUri)
            conn.protocol_version = 3
            conn.set_option(ldap.OPT_REFERRALS, 0)

            # make the connection
            conn.simple_bind_s('%s@%s' % (userName, ldapSetting.ldapDomain), password)
            ldapFilter = "(&(objectCategory=person)(objectClass=user)(sAMAccountName=%s))" % userName

            dcParts = ','.join(['DC=%s' % part
                                for part in ldapSetting.ldapDomain.split('.')])

            ldapBases = []
            if ldapSetting.ldapOUFolders:
                ldapBases += self._makeLdapBase(ldapSetting.ldapOUFolders, userName, "OU")
            if ldapSetting.ldapCNFolders:
                ldapBases += self._makeLdapBase(ldapSetting.ldapCNFolders, userName, "CN")

            if not ldapBases:
                raise LoginFailed("LDAP OU and/or CN search paths must be set.")

            userDetails = None
            for ldapBase in ldapBases:
                ldapBase = "%s,%s" % (ldapBase, dcParts)

                try:
                    # Example Base : 'CN=atuser1,CN=Users,DC=synad,DC=synerty,DC=com'
                    userDetails = conn.search_st(ldapBase, ldap.SCOPE_SUBTREE,
                                                 ldapFilter, None, 0, 10)

                    if userDetails:
                        break

                except ldap.NO_SUCH_OBJECT:
                    logger.warning("CN or OU doesn't exist : %s", ldapBase)

        except ldap.NO_SUCH_OBJECT:
            raise LoginFailed(
                "An internal error occurred, ask admin to check Attune logs")

        except ldap.INVALID_CREDENTIALS:
            raise LoginFailed("Username or password is incorrect")

        if not userDetails:
            raise LoginFailed("User doesn't belong to the correct CN/OUs")

        userDetails = userDetails[0][1]

        groups = []
        for memberOf in userDetails['memberOf']:
            group = memberOf.decode().split(',')[0]
            if '=' in group:
                group = group.split('=')[1]
            groups.append(group)

        userTitle = None
        if userDetails['displayName']:
            userTitle = userDetails['displayName'][0].decode()

        email = None
        if userDetails['userPrincipalName']:
            email = userDetails['userPrincipalName'][0].decode()

        userUuid = None
        if userDetails['distinguishedName']:
            userUuid = userDetails['distinguishedName'][0].decode()

        if ldapSetting.ldapGroups:
            ldapGroups = set([s.strip() for s in ldapSetting.ldapGroups.split(',')])

            if not ldapGroups & set(groups):
                raise LoginFailed("User is not apart of an authorised group")

        self._makeOrCreateInternalUserBlocking(dbSession,
                                               userName, userTitle, userUuid, email,
                                               ldapSetting.ldapTitle)

        return groups