Beispiel #1
0
    def authenticate(cls, username=None, password=None):
        """
        Return 2tuple, representing authorized user or (None,None) on fail

        First tuple element is String ID representing a distingushed
         name for external users, e.g.:
        "[email protected],ou=People,o=warehouse-external"
        
        Second tuple element is Dict, representing a decoded ODIC access
         token

        >>> b = KeycloakBackend()
        >>> b.authenticate()
        (None, None)
        >>> # Check username
        >>> b.authenticate("*****@*****.**", "anything")
        (None, None)
        >>> # Check bogus username
        >>> b.authenticate("Test", "anything")
        (None, None)
        """
        user_identifier = None
        decoded_token = None
        if username is not None:
            u = None #auth may have failed: prepare to return None

            # setup OIDC client
            conf = config_loader.get_api_config()
            auth_url = conf['external.keycloak.url']
            dw_client_id = conf['external.keycloak.warehouse_client_id']
            auth_client = KeycloakOpenID(server_url=auth_url
                ,client_id=dw_client_id, realm_name="master"
                ,client_secret_key="value-doesnt-matter"
                ,verify=False)

            try:
                # login
                encoded_token = auth_client.token(username, password)
                # get server public key
                iss_url = auth_url+'realms/master'
                json_iss = requests.get(iss_url, verify=False).json()
                pub_key = add_pem_headfoot(json_iss['public_key'])
                # decode server response
                decode_opts = {"verify_signature":True, "verify_aud": True, "exp": True}
                decoded_token = auth_client.decode_token(
                     encoded_token['access_token']
                    ,key=pub_key, options=decode_opts)
                user_dn = oidc_token_to_ldap_style_name(decoded_token)
                #login is valid
                user_identifier = user_dn #return
            except KeycloakAuthenticationError:
                pass #login is invalid
        return user_identifier, decoded_token
Beispiel #2
0
def load_user_from_request(request):
    if request.path != "/" and not app.config.get('is_local') and os.environ.get("TESTING") is None:
        try:
            api_key = request.headers.get('Authorization')
            bearer_token = api_key.replace('Bearer ', '', 1)
            keycloak_openid = KeycloakOpenID(
                                            server_url=app.config.get('authentication', {}).get('url'),
                                            client_id=app.config.get('authentication', {}).get('clientid'),
                                            realm_name=app.config.get('authentication', {}).get('realm')
                                        )

            keycloak_public_key = "-----BEGIN PUBLIC KEY-----\n" + keycloak_openid.public_key() + "\n-----END PUBLIC KEY-----"
            options = {"verify_signature": True, "verify_aud": True, "verify_exp": True}
            token_info = keycloak_openid.decode_token(bearer_token, key=keycloak_public_key, options=options)
            return Auth.authenticate_user(token_info, app.config.get('authentication'))
        except Exception as e:
            logging.exception(e)
            return make_response("Error occured while authentication: ", str(e), 500)
    else:
        return User(is_authenticated=True)
Beispiel #3
0
def validate(token):
    KEYCLOAK_PUBLIC_KEY = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvgTcVSXga3C6Tam96Q2kmnQ7U55f0hA/56HRQnsY9V0d21QlLkkD+DvIbKGaDVNLp8I+Gqlx4s6u5oDKIA12BbE9jAWz7nblKJUMjESDbpQJVivLAOXPvHQK8tCvxClp8dLEb9cEGXbabO+YIWN3pNn5CL8l3IBbJs1sT76G3jyo0sw5oiSRj0agxIPrtThDPDKhOqG0V+NYtudeE1FhWzt4ajYS4yIHGioVtYsvTfePMRGLRHTMQPjia8kBwnVRDwqMputLGfBg8qnz/xUPiRj5hmEeLuT73zte/CLmfSyqn7IsOrso1zEXgBBLz8XUsp1elGRz+zZtkHqnPot8EQIDAQAB"
    KEYCLOAK_PUBLIC_KEY = f"-----BEGIN PUBLIC KEY-----\n{KEYCLOAK_PUBLIC_KEY}\n-----END PUBLIC KEY-----"

    options = {"verify_signature": True, "exp": True}
    keycloak_openid = KeycloakOpenID(
        server_url="http://localhost:8080/auth/",
        client_id="api",
        realm_name="master",
        client_secret_key="05e8bfbf-e050-4830-bc8a-d5f4376b13ff")
    try:
        token_info = keycloak_openid.decode_token(token,
                                                  KEYCLOAK_PUBLIC_KEY,
                                                  options=options)
        if (token_info):
            return True
    except jwt.JWTClaimsError:
        return False
    except jwt.ExpiredSignatureError:
        return False
    except jwt.JWSError:
        return False
Beispiel #4
0
class _Keycloak(AuthProvider):
    f"""
    Redis key-value pair database implementation of KeyValueStore
    
    Defines methods for getting, deleting and putting key value pairs
    
    :param host, port, db (see also `https://github.com/andymccurdy/redis-py)`
    Example:
    ```
        db = KeyValueDatabase.instance(provider='redis', host='localhost', port=6379, db=0)
    ```
    """
    def __init__(self,
                 domain: Optional[str] = None,
                 audience: Optional[str] = None):

        super().__init__()
        self._domain = domain or os.getenv('KEYCLOAK_DOMAIN')
        self._client_secret_key = os.getenv('KEYCLOAK_CLIENT_SECRET_KEY')
        self._client_secret_id = os.getenv('KEYCLOAK_CLIENT_SECRET_ID')
        self._realm = os.getenv('KEYCLOAK_REALM')
        self._audience = audience or os.getenv('XCUBE_HUB_OAUTH_AUD')
        self._algorithms = ["RS256"]

        if self._domain is None:
            raise Unauthorized(description="Keycloak error: Domain not set")

        if self._audience is None:
            raise Unauthorized(description="Keycloak error: Audience not set")

        self._keycloak_openid = KeycloakOpenID(
            server_url=f"https://{self._domain}/auth/",
            client_id=self._client_secret_id,
            realm_name=self._realm,
            client_secret_key=self._client_secret_key,
            verify=True)

    def get_email(self, claims):
        if 'email' not in claims:
            return "no email"
        return claims['email']

    def verify_token(self, token: str) -> Dict:
        """
        Get a key value
        :param token:
        :return:
        """

        try:
            self._keycloak_openid.introspect(token)
        except KeycloakGetError as e:
            raise Unauthorized(description="invalid token: " + str(e))

        certs = self._keycloak_openid.certs()
        rsa_key = {}
        for key in certs["keys"]:
            rsa_key = {
                "kty": key["kty"],
                "kid": key["kid"],
                "use": key["use"],
                "n": key["n"],
                "e": key["e"]
            }

        try:
            self._claims = self._keycloak_openid.decode_token(token=token,
                                                              key=rsa_key)
        except Exception as e:
            raise Unauthorized(description=str(e))

        return self._claims
Beispiel #5
0
    def check_password(self, user_id, password):
        """ Attempt to authenticate a user against an Keycloak Server
            and register an account if none exists.

            Returns:
                True if authentication against Keycloak was successful
        """
        if not password:
            defer.returnValue(False)

        localpart = user_id.split(":", 1)[0][1:]
        logger.info("! %s", localpart)

        keycloak_openid = KeycloakOpenID(server_url=self.url,
                                         client_id=self.client_id,
                                         realm_name=self.realm_name,
                                         client_secret_key=self.secret_key)
        logger.debug("Attempting Keycloak connection with %s", self.url)

        try:
            token = yield keycloak_openid.token(localpart, password)
        except KeycloakAuthenticationError as e:
            logger.info("Failed login attempt %s error: %s", localpart, e)
            defer.returnValue(False)

        logger.info("User %s authenticated", user_id)
        options = {"verify_signature": True, "verify_aud": True, "exp": True}

        key = self.public_key
        if self.algorithm == 'RS256':
            key = '-----BEGIN PUBLIC KEY-----\n' + key + '\n-----END PUBLIC KEY-----'

        token_info = keycloak_openid.decode_token(token['access_token'],
                                                  key=key,
                                                  algorithms=[self.algorithm],
                                                  options=options)

        if not (yield self.account_handler.check_user_exists(user_id)):
            logger.info("User %s does not exist yet, creating...", user_id)
            if localpart != localpart.lower() and self.regLower:
                logger.info(
                    'User %s was cannot be created due to username lowercase policy',
                    localpart)
                defer.returnValue(False)
            user_id, access_token = (yield self.account_handler.register(
                localpart=localpart))
            registration = True
            logger.info(
                "Registration based on REST data was successful for %s",
                user_id)
        else:
            logger.info("User %s already exists, registration skipped",
                        user_id)

        if bool(self.profile_attrs):
            logger.info("profile attrs")
            store = yield self.account_handler.hs.get_profile_handler().store
            profile = {}
            for key, alias in self.profile_attrs.items():
                if alias in token_info:
                    profile[key] = token_info[alias]

            display_name = profile.pop('display_name', None)
            if display_name:
                logger.info(
                    "Setting display name to '%s' based on profile data",
                    display_name)
                yield store.set_profile_displayname(localpart, display_name)
            logger.info("end profile attrs")

            # TODO 3pids
        else:
            logger.info("No profile data")

        def _save_keycloak_token(txn):
            template = """
INSERT INTO keycloak_provider_tokens
(user_id, refresh_tokens)
VALUES
('{0}', ARRAY['{1}'])
ON CONFLICT (user_id) DO
UPDATE SET
refresh_tokens = array_append(keycloak_provider_tokens.refresh_tokens, '{1}')
"""
            sql = template.format(user_id, token['refresh_token'])
            txn.execute(sql)

        self.account_handler.run_db_interaction("save_keycloak_token",
                                                _save_keycloak_token)
        logger.info("insert end")
        defer.returnValue(True)
Beispiel #6
0
# Get RPT (Entitlement)
token = keycloak_openid.token("user", "password")
rpt = keycloak_openid.entitlement(token['access_token'], "resource_id")

# Instropect RPT
token_rpt_info = keycloak_openid.introspect(keycloak_openid.introspect(token['access_token'], rpt=rpt['rpt'],
                                     token_type_hint="requesting_party_token"))

# Introspect Token
token_info = keycloak_openid.introspect(token['access_token']))

# Decode Token
KEYCLOAK_PUBLIC_KEY = keycloak_openid.public_key()
options = {"verify_signature": True, "verify_aud": True, "exp": True}
token_info = keycloak_openid.decode_token(token['access_token'], key=KEYCLOAK_PUBLIC_KEY, options=options)

# Get permissions by token
token = keycloak_openid.token("user", "password")
keycloak_openid.load_authorization_config("example-authz-config.json")
policies = keycloak_openid.get_policies(token['access_token'], method_token_info='decode', key=KEYCLOAK_PUBLIC_KEY)
permissions = keycloak_openid.get_permissions(token['access_token'], method_token_info='introspect')

# KEYCLOAK ADMIN

from keycloak import KeycloakAdmin

keycloak_admin = KeycloakAdmin(server_url="http://localhost:8080/auth/",
                               username='******',
                               password='******',
                               realm_name="example_realm",