Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds support for the ExcludeIssuerFromAuthResponse option on OpenIdClient #934

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

sebght
Copy link

@sebght sebght commented Feb 28, 2024

Solves #928

What has been changed :

  • new ExcludeIssuerFromAuthResponse attribute in the client
  • the resource schema is updated with ExcludeIssuerFromAuthResponse attribute
  • the datasource openid_client also returns this attribute
  • tests were updated + one new test case to check that the option is well updated
  • provider documentation returns the new attribute with the same words Keycloak uses

@paul-pch
Copy link

paul-pch commented Mar 4, 2024

I've been struggling with this exat configuration. Thanks for the PR !

@sebght
Copy link
Author

sebght commented Mar 5, 2024

My tests have led me to think that the option is changed to false until it's not explicitly given to the API... This means everytime you terraform apply your client resource, it will set it back to false...
Kind of a thorn in the side for ppl I guess (for me it is 😅). Should we consider a v4.4.1 @mrparkers ?

@sebght
Copy link
Author

sebght commented Mar 5, 2024

Nevermind, I did more precise tests and they prove that the option is not overwritten every apply. Sorry for the spamming !

@bturos
Copy link

bturos commented Sep 19, 2024

Hi @sebght ! Thanks for working on adding support for this option
Any chance this could be merged soon? This would help a lot in my current project 🙂

@mwalczykpl
Copy link

Good job @sebght, can't wait to see that released! 🚀

@sebght
Copy link
Author

sebght commented Sep 19, 2024

Well the repository is quite on pause currently, but I hope that this will change and that this PR will move on ! Follow the news here : #964

@B3ns44d
Copy link

B3ns44d commented Oct 1, 2024

Hello @sebght, Thank you for the merge request. We're currently encountering this issue after upgrading Keycloak to the latest version. Is there any chance this could be merged soon? In the meantime, we've created a script as a workaround until an official release is available.

import requests
import argparse
import logging
from typing import List, Optional

# Configure logging
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    datefmt='%Y-%m-%d %H:%M:%S'
)
logger = logging.getLogger(__name__)


def get_token(idp_host: str, username: str, password: str) -> Optional[str]:
    """Get access token from the Identity Provider"""
    url = f"{idp_host}/realms/master/protocol/openid-connect/token"
    payload = {
        "username": username,
        "password": password,
        "grant_type": "password",
        "client_id": "admin-cli",
    }
    headers = {"Content-Type": "application/x-www-form-urlencoded"}

    response = requests.post(url, data=payload, headers=headers)

    if response.status_code == 200:
        logger.info("Successfully obtained token")
        return response.json().get("access_token")
    else:
        logger.error(f"Failed to get token: {response.status_code}, {response.text}")
        return None


def get_realms(idp_host: str, token: str) -> List[str]:
    """Fetch a list of realms"""
    url = f"{idp_host}/admin/realms"
    headers = {"Authorization": f"Bearer {token}"}

    response = requests.get(url, headers=headers)

    if response.status_code == 200:
        logger.info("Successfully fetched realms")
        realms = response.json()
        return [realm["realm"] for realm in realms]
    else:
        logger.error(f"Failed to fetch realms: {response.status_code}, {response.text}")
        return []


def get_client_ids(idp_host: str, realm: str, token: str) -> List[str]:
    """Fetch client IDs that contain 'ui' in their clientId"""
    url = f"{idp_host}/admin/realms/{realm}/clients"
    headers = {"Authorization": f"Bearer {token}"}

    response = requests.get(url, headers=headers)

    if response.status_code == 200:
        logger.info(f"Successfully fetched clients for realm {realm}")
        clients = response.json()
        return [client["id"] for client in clients if "ui" in client["clientId"]]
    else:
        logger.error(f"Failed to fetch clients for realm {realm}: {response.status_code}, {response.text}")
        return []


def update_client(idp_host: str, realm: str, client_id: str, token: str) -> None:
    """Update the client's attribute to exclude issuer from the URL"""
    url = f"{idp_host}/admin/realms/{realm}/clients/{client_id}"
    headers = {"Authorization": f"Bearer {token}"}

    # Fetch client details
    response = requests.get(url, headers=headers)

    if response.status_code == 200:
        client_details = response.json()
        client_name = client_details["clientId"]
        logger.info(f"Updating client {client_name} in realm {realm}...")

        # Update client attribute
        client_details["attributes"]["exclude.issuer.from.auth.response"] = "true"

        # Send the updated client details
        update_response = requests.put(
            url, headers=headers, json=client_details
        )

        if update_response.status_code == 204:
            logger.info(f"Client {client_name} updated successfully.")
        else:
            logger.error(
                f"Failed to update client {client_name}: {update_response.status_code}, {update_response.text}")
    else:
        logger.error(f"Failed to fetch client details: {response.status_code}, {response.text}")


def main(idp_host: str, username: str, password: str) -> None:
    """Main function to handle the logic of fetching realms and updating clients"""
    token = get_token(idp_host, username, password)

    if not token:
        logger.error("Failed to retrieve token. Exiting.")
        return

    realms = get_realms(idp_host, token)
    if not realms:
        logger.error("No realms found. Exiting.")
        return

    for realm in realms:
        logger.info(f"Processing realm: {realm}")
        client_ids = get_client_ids(idp_host, realm, token)

        for client_id in client_ids:
            update_client(idp_host, realm, client_id, token)


if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Keycloak Client Attribute Updater")
    parser.add_argument("--host", required=True, help="Identity Provider host URL")
    parser.add_argument("-u", "--username", required=True, help="Username for authentication")
    parser.add_argument("-p", "--password", required=True, help="Password for authentication")

    args = parser.parse_args()

    main(args.host, args.username, args.password)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants