Beispiel #1
0
 def from_kc_oidc_json(app,
                       redirect_uri,
                       config_path=None,
                       logout_path=None,
                       heartbeat_path=None,
                       keycloak_kwargs=None,
                       authorization_settings=None,
                       uri_whitelist=None,
                       login_path=None):
     # Read config, assumed to be in Keycloak OIDC JSON format.
     config_path = "keycloak.json" if config_path is None else config_path
     with open(config_path, 'r') as f:
         config_data = json.load(f)
     # Setup the Keycloak connection.
     keycloak_config = dict(
         server_url=config_data["auth-server-url"],
         realm_name=config_data["realm"],
         client_id=config_data["resource"],
         client_secret_key=config_data["credentials"]["secret"],
         verify=config_data["ssl-required"] != "none")
     if keycloak_kwargs is not None:
         keycloak_config = {**keycloak_config, **keycloak_kwargs}
     keycloak_openid = KeycloakOpenID(**keycloak_config)
     if authorization_settings is not None:
         keycloak_openid.load_authorization_config(authorization_settings)
     return FlaskKeycloak(app,
                          keycloak_openid,
                          redirect_uri,
                          logout_path=logout_path,
                          heartbeat_path=heartbeat_path,
                          uri_whitelist=uri_whitelist,
                          login_path=login_path)
Beispiel #2
0
class KeycloakMiddleware(MiddlewareMixin):

    def __init__(self, get_response):
        """

        :param get_response:
        """

        self.config = settings.APPLICATION_CONFIG

        # Read configurations
        try:
            self.server_url = get_application_config_for_key('KEYCLOAK_SERVER_URL')
            self.client_id = get_application_config_for_key('KEYCLOAK_CLIENT_ID')
            self.realm = get_application_config_for_key('KEYCLOAK_REALM')
        except KeyError as e:
            raise Exception("KEYCLOAK_SERVER_URL, KEYCLOAK_CLIENT_ID or KEYCLOAK_REALM not found.")

        self.client_secret_key = get_application_config_for_key('KEYCLOAK_CLIENT_SECRET_KEY')
        self.default_access = get_application_config_for_key('KEYCLOAK_DEFAULT_ACCESS')
        self.method_validate_token = get_application_config_for_key('KEYCLOAK_METHOD_VALIDATE_TOKEN')
        self.keycloak_authorization_config = get_application_config_for_key('KEYCLOAK_AUTHORIZATION_CONFIG')

        # Create Keycloak instance
        self.keycloak = KeycloakOpenID(server_url=self.server_url,
                                       client_id=self.client_id,
                                       realm_name=self.realm,
                                       client_secret_key=self.client_secret_key,
                                       )

        # Read policies
        if self.keycloak_authorization_config:
            self.keycloak.load_authorization_config(self.keycloak_authorization_config)

        # Django
        self.get_response = get_response

        print("KEYCLOAK_SERVER URL", get_application_config_for_key('KEYCLOAK_SERVER_URL'))
        print("KEYCLOAK_CLIENT_ID URL", get_application_config_for_key('KEYCLOAK_CLIENT_ID'))
        print("KEYCLOAK_REALM URL", get_application_config_for_key('KEYCLOAK_REALM'))


    @property
    def keycloak(self):
        return self._keycloak

    @keycloak.setter
    def keycloak(self, value):
        self._keycloak = value

    @property
    def config(self):
        return self._config

    @config.setter
    def config(self, value):
        self._config = value

    @property
    def server_url(self):
        return self._server_url

    @server_url.setter
    def server_url(self, value):
        self._server_url = value

    @property
    def client_id(self):
        return self._client_id

    @client_id.setter
    def client_id(self, value):
        self._client_id = value

    @property
    def client_secret_key(self):
        return self._client_secret_key

    @client_secret_key.setter
    def client_secret_key(self, value):
        self._client_secret_key = value

    @property
    def client_public_key(self):
        return self._client_public_key

    @client_public_key.setter
    def client_public_key(self, value):
        self._client_public_key = value

    @property
    def realm(self):
        return self._realm

    @realm.setter
    def realm(self, value):
        self._realm = value

    @property
    def keycloak_authorization_config(self):
        return self._keycloak_authorization_config

    @keycloak_authorization_config.setter
    def keycloak_authorization_config(self, value):
        self._keycloak_authorization_config = value

    @property
    def method_validate_token(self):
        return self._method_validate_token

    @method_validate_token.setter
    def method_validate_token(self, value):
        self._method_validate_token = value

    def __call__(self, request):
        """
        :param request:
        :return:
        """
        return self.get_response(request)

    def process_view(self, request, view_func, view_args, view_kwargs):
        """
        Validate only the token introspect.
        :param request: django request
        :param view_func:
        :param view_args: view args
        :param view_kwargs: view kwargs
        :return:
        """

        print ('DEBUG:', request)

        # do not block the access to root!
        if request.path_info == '/':
            logger.debug('** exclude path found, skipping')
            return None
        
        # CONTINGENCY SOLUTION!!! review API to avoid this
        # if request.path_info[-7:] == '/series':
        #     return None

        whitelist = [
             '/api/v1/api',
             '/api/v1/alive',
             '/api/v1/ready',
             '/oformat/WORD/documentation',
             '/oformat/EXCEL/documentation', 
             '/export_download'
        ]
        for iwhite in whitelist:
            if request.path_info.endswith(iwhite):
                print ('skipped:', request.path_info)
                return None

        if hasattr(settings, 'KEYCLOAK_BEARER_AUTHENTICATION_EXEMPT_PATHS'):
            path = request.path_info.lstrip('/')

            if any(re.match(m, path) for m in
                   settings.KEYCLOAK_BEARER_AUTHENTICATION_EXEMPT_PATHS):
                logger.debug('** exclude path found, skipping')
                return None

        try:
            roles = view_func.cls.roles
        except AttributeError as e:
            return JsonResponse({"detail": NotAuthenticated.default_detail}, status=NotAuthenticated.status_code)

        if 'HTTP_AUTHORIZATION' not in request.META:
            return JsonResponse({"detail": NotAuthenticated.default_detail}, status=NotAuthenticated.status_code)

        auth_header = request.META.get('HTTP_AUTHORIZATION').split()
        token = auth_header[1] if len(auth_header) == 2 else auth_header[0]

        try:
            userinfo = self.keycloak.userinfo(token)
        except KeycloakError as e:
            return JsonResponse({"detail": AuthenticationFailed.default_detail}, status=AuthenticationFailed.status_code)

        has_role = True
        for role in roles:
            if role not in userinfo['groups']:
                has_role = False

        # In case we need to verify token. But since we verify it by using userinfo call, it is not necessary atm.
        # KEYCLOAK_PUBLIC_KEY = self.client_public_key
        # options = {"verify_signature": True, "verify_aud": True, "verify_exp": True}
        # token_info = self.keycloak.decode_token(token, key=KEYCLOAK_PUBLIC_KEY, options=options)

        # if token_info['realm_access'] and token_info['realm_access']['roles']:
        #     print(token_info['realm_access']['roles'])

        if has_role:
            return None

        # User Permission Denied
        return JsonResponse({"detail": PermissionDenied.default_detail}, status=PermissionDenied.status_code)
Beispiel #3
0
class KeycloakMiddleware(MiddlewareMixin):

    def __init__(self, get_response):
        """
        :param get_response:
        """

        self.config = settings.KEYCLOAK_CONFIG

        # Read configurations
        try:
            self.server_url = self.config['KEYCLOAK_SERVER_URL']
            self.client_id = self.config['KEYCLOAK_CLIENT_ID']
            self.realm = self.config['KEYCLOAK_REALM']
        except KeyError as e:
            raise Exception("KEYCLOAK_SERVER_URL, KEYCLOAK_CLIENT_ID or KEYCLOAK_REALM not found.")

        self.client_secret_key = self.config.get('KEYCLOAK_CLIENT_SECRET_KEY', None)
        self.client_public_key = self.config.get('KEYCLOAK_CLIENT_PUBLIC_KEY', None)
        self.default_access = self.config.get('KEYCLOAK_DEFAULT_ACCESS', "DENY")
        self.method_validate_token = self.config.get('KEYCLOAK_METHOD_VALIDATE_TOKEN', "INTROSPECT")
        self.keycloak_authorization_config = self.config.get('KEYCLOAK_AUTHORIZATION_CONFIG', None)

        # Create Keycloak instance
        self.keycloak = KeycloakOpenID(server_url=self.server_url,
                                       client_id=self.client_id,
                                       realm_name=self.realm,
                                       client_secret_key=self.client_secret_key)

        # Read policies
        if self.keycloak_authorization_config:
            self.keycloak.load_authorization_config(self.keycloak_authorization_config)

        # Django
        self.get_response = get_response
            

    @property
    def keycloak(self):
        return self._keycloak

    @keycloak.setter
    def keycloak(self, value):
        self._keycloak = value

    @property
    def config(self):
        return self._config

    @config.setter
    def config(self, value):
        self._config = value

    @property
    def server_url(self):
        return self._server_url

    @server_url.setter
    def server_url(self, value):
        self._server_url = value

    @property
    def client_id(self):
        return self._client_id

    @client_id.setter
    def client_id(self, value):
        self._client_id = value

    @property
    def client_secret_key(self):
        return self._client_secret_key

    @client_secret_key.setter
    def client_secret_key(self, value):
        self._client_secret_key = value

    @property
    def client_public_key(self):
        return self._client_public_key

    @client_public_key.setter
    def client_public_key(self, value):
        self._client_public_key = value

    @property
    def realm(self):
        return self._realm

    @realm.setter
    def realm(self, value):
        self._realm = value

    @property
    def keycloak_authorization_config(self):
        return self._keycloak_authorization_config

    @keycloak_authorization_config.setter
    def keycloak_authorization_config(self, value):
        self._keycloak_authorization_config = value

    @property
    def method_validate_token(self):
        return self._method_validate_token

    @method_validate_token.setter
    def method_validate_token(self, value):
        self._method_validate_token = value

    def __call__(self, request):
        """
        :param request:
        :return:
        """
        return self.get_response(request)

    def process_view(self, request, view_func, view_args, view_kwargs):
        """
        Validate only the token introspect.
        :param request: django request
        :param view_func:
        :param view_args: view args
        :param view_kwargs: view kwargs
        :return:
        """
        
        if hasattr(settings, 'KEYCLOAK_BEARER_AUTHENTICATION_EXEMPT_PATHS'):
            path = request.path_info.lstrip('/')
 
            if any(re.match(m, path) for m in
                   settings.KEYCLOAK_BEARER_AUTHENTICATION_EXEMPT_PATHS):
                logger.debug('** exclude path found, skipping')
                return None

        try:
            view_scopes = view_func.cls.keycloak_scopes
        except AttributeError as e:
            logger.debug('Allowing free acesss, since no authorization configuration (keycloak_scopes) found for this request route :%s',request)
            return None

        if 'HTTP_AUTHORIZATION' not in request.META:
            return JsonResponse({"detail": NotAuthenticated.default_detail},
                                status=NotAuthenticated.status_code)

        auth_header = request.META.get('HTTP_AUTHORIZATION').split()
        token = auth_header[1] if len(auth_header) == 2 else auth_header[0]

        # Get default if method is not defined.
        required_scope = view_scopes.get(request.method, None) \
            if view_scopes.get(request.method, None) else view_scopes.get('DEFAULT', None)

        # DEFAULT scope not found and DEFAULT_ACCESS is DENY
        if not required_scope and self.default_access == 'DENY':
            return JsonResponse({"detail": PermissionDenied.default_detail},
                                status=PermissionDenied.status_code)

        try:
            user_permissions = self.keycloak.get_permissions(token,
                                                             method_token_info=self.method_validate_token.lower(),
                                                             key=self.client_public_key)
        except KeycloakInvalidTokenError as e:
            return JsonResponse({"detail": AuthenticationFailed.default_detail},
                                status=AuthenticationFailed.status_code)

        for perm in user_permissions:
            if required_scope in perm.scopes:
                return None

        # User Permission Denied
        return JsonResponse({"detail": PermissionDenied.default_detail},
                            status=PermissionDenied.status_code)
Beispiel #4
0
class KeycloakMiddleware(object):

    header_key = "HTTP_AUTHORIZATION"
    set_session_state_cookie = True

    def __init__(self, get_response):

        self.config = settings.KEYCLOAK_CONFIG

        # Read configurations
        try:
            self.server_url = self.config['KEYCLOAK_SERVER_URL']
            self.client_id = self.config['KEYCLOAK_CLIENT_ID']
            self.realm = self.config['KEYCLOAK_REALM']
        except KeyError:
            raise Exception('KEYCLOAK_SERVER_URL, KEYCLOAK_CLIENT_ID or '
                            'KEYCLOAK_REALM not found.')

        self.client_secret_key = self.config.get('KEYCLOAK_CLIENT_SECRET_KEY',
                                                 None)
        self.client_public_key = self.config.get('KEYCLOAK_CLIENT_PUBLIC_KEY',
                                                 None)
        self.default_access = self.config.get('KEYCLOAK_DEFAULT_ACCESS',
                                              'DENY')
        self.method_validate_token = self.config.get(
            'KEYCLOAK_METHOD_VALIDATE_TOKEN', 'INTROSPECT')
        self.keycloak_authorization_config = self.config.get(
            'KEYCLOAK_AUTHORIZATION_CONFIG', None)

        # Create Keycloak instance
        self.keycloak = KeycloakOpenID(
            server_url=self.server_url,
            client_id=self.client_id,
            realm_name=self.realm,
            client_secret_key=self.client_secret_key,
        )

        # Read policies
        if self.keycloak_authorization_config:
            self.keycloak.load_authorization_config(
                self.keycloak_authorization_config)

        # Django
        self.get_response = get_response

    def __call__(self, request):
        # Code to be executed for each request before
        # the view (and later middleware) are called.

        response = self.get_response(request)

        # Code to be executed for each request/response after
        # the view is called.

        return response

    def process_view(self, request, view_func, view_args, view_kwargs):
        """
        Validate only the token introspect.
        :param request: django request
        :param view_func: view function
        :param view_args: view args
        :param view_kwargs: view kwargs
        :return: JSON response, HTML response or None
        """

        # Load permissions from Keycloak
        keycloak_permissions = self.get_keycloak_permissions(request)

        # Set permissions for anonymous user
        if keycloak_permissions is None:
            keycloak_permissions = {
                'acquisition_mode:view', 'authority:view',
                'acquisition_mode:view', 'collection_language:view',
                'collection_location:view', 'collection_publisher:view',
                'collection:view', 'collectioncollectors:view', 'coupe:view',
                'dance:view', 'document_fonds:view',
                'document_institution:view', 'document_mission:view',
                'document_collection:view', 'document_item:view',
                'domain_music:view', 'domain_song:view', 'domain_tale:view',
                'domain_vocal:view', 'emit_vox:view', 'fond:view',
                'hornbostelsachs:view', 'institution:view', 'instrument:view',
                'item_author:view', 'item_coirault:view',
                'item_collector:view', 'item_compositor:view',
                'item_dance:view', 'item_domain_music:view',
                'item_domain_song:view', 'item_domain_tale:view',
                'item_domain_vocal:view', 'item_informer:view',
                'item_language:view', 'item_musical_group:view',
                'item_musical_organization:view', 'item_performance:view',
                'item_thematic:view', 'item_usefulness:view', 'item:view',
                'language:view', 'legal_rights:view', 'location_gis:view',
                'location_gis_collection:view', 'mediatype:view',
                'metadata_author:view', 'mission:view', 'musical_group:view',
                'musical_organization:view',
                'performance_collection_musician:view',
                'performance_collection:view', 'publisher:view',
                'recording_context:view', 'skos_collection:view',
                'skos_concept:view', 'thematic:view', 'usefulness:view'
            }

        # Build Django permissions
        request.user._perm_cache = {
            'keycloak.%s' % perm.replace(':', '_')
            for perm in keycloak_permissions
        }

        # Extract required Keycloak permissions from view
        view_class = getattr(view_func, 'cls', None) \
            or getattr(view_func, 'view_class', None)
        view_scopes = getattr(view_class, 'keycloak_scopes', None)
        if view_scopes is None:
            logger.debug(
                'Allowing free access, since no authorization '
                'configuration (keycloak_scopes) found for this '
                'request route: %s', request)
            return None

        # Get DEFAULT required permission if method is not defined
        required_permission = view_scopes.get(request.method, None) \
            or view_scopes.get('DEFAULT', None)

        # DEFAULT scope not found and DEFAULT_ACCESS is DENY
        if not required_permission and self.default_access == 'DENY':
            return self.create_permission_denied_response(request, view_class)

        # Check required permission from user Keycloak permissions
        if required_permission not in keycloak_permissions:
            return self.create_permission_denied_response(request, view_class)

    def create_permission_denied_response(self, request, view_class):
        """
        Create permission denied response.
        :param request: django request
        :param view_class: class of view function
        :return: JSON response or HTML response
        """

        # Create HTML response (Front request)
        default_403_error_message = getattr(view_class,
                                            'default_403_error_message', None)
        if default_403_error_message is not None:
            return render(
                request,
                'error.html', {
                    'exception': default_403_error_message
                    or _('Accès interdit.'),
                },
                status=403)

        # Create JSON response (Back request)
        return JsonResponse(
            {'detail': PermissionDenied.default_detail},
            status=PermissionDenied.status_code,
        )

    def get_keycloak_permissions(self, request):
        """
        Extract permissions from Keycloak for current user.
        :param request: django request
        :return: set of permissions (as string) or None for anonymous user
        """

        # Get session token
        access_token = request.session.get('oidc_access_token', '')
        if not access_token:
            try:
                access_token = request.META['HTTP_AUTHORIZATION'].split(' ')[1]
            except:
                return None

        # No token is anonymous user: unknown permissions
        if not access_token:
            return None

        # Get permissions from Keycloak
        try:
            permissions = self.keycloak.get_permissions(access_token)
        except KeycloakInvalidTokenError:
            return None

        # Extract permissions from scopes
        return {p for perm in permissions for p in perm.scopes}
Beispiel #5
0
# 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",
                               client_secret_key="client-secret",
                               verify=True)
        
# Add user