예제 #1
0
    def register_client(self):
        # Generate client dynamically if one is not configured.
        if not self.client_id or not self.client_secret:
            print ("NOTICE: Client not found, generating one... ")
            scim_client = EOEPCA_Scim(self.server_url)
            new_client = scim_client.registerClient("PDP Dynamic Client",
                                        grantTypes = ["client_credentials"],
                                        redirectURIs = [""],
                                        logoutURI = "", 
                                        responseTypes = ["code","token","id_token"],
                                        scopes = ['openid', 'uma_protection', 'permission'],
                                        token_endpoint_auth_method = ENDPOINT_AUTH_CLIENT_POST)
            print("NEW CLIENT created with ID '"+new_client["client_id"]+"', since no client config was found on config.json or environment")

            self.client_id = new_client["client_id"]
            self.client_secret = new_client["client_secret"]
            return self.client_id, self.client_secret
예제 #2
0
 def scim_client_get_details(self):
     if self.details == None:
         print("Registering new client")
         scim_client = EOEPCA_Scim(self.hostname)
         self.client = scim_client.registerClient(
             "UMA Flow Test Client",
             grantTypes=[
                 "client_credentials", "password",
                 "urn:ietf:params:oauth:grant-type:uma-ticket"
             ],
             redirectURIs=[""],
             logoutURI="",
             responseTypes=["code", "token", "id_token"],
             scopes=[
                 'openid', 'email', 'user_name ', 'uma_protection',
                 'permission'
             ],
             token_endpoint_auth_method=ENDPOINT_AUTH_CLIENT_POST)
         self.details = {}
         self.details["client_id"] = self.client["client_id"]
         self.details["client_secret"] = self.client["client_secret"]
     else:
         print("Reusing existing client")
     return self.details
예제 #3
0
def get_config(config_path: str):
    """
    Loads entire configuration onto memory
    """
    env_vars = [
        "PEP_REALM", "PEP_AUTH_SERVER_URL", "PEP_SERVICE_HOST",
        "PEP_PROXY_SERVICE_PORT", "PEP_RESOURCES_SERVICE_PORT",
        "PEP_S_MARGIN_RPT_VALID", "PEP_CHECK_SSL_CERTS", "PEP_USE_THREADS",
        "PEP_DEBUG_MODE", "PEP_RESOURCE_SERVER_ENDPOINT",
        "PEP_API_RPT_UMA_VALIDATION", "PEP_RPT_LIMIT_USES", "PEP_PDP_URL",
        "PEP_PDP_PORT", "PEP_PDP_POLICY_ENDPOINT", "PEP_VERIFY_SIGNATURE"
    ]

    use_env_var = True

    for env_var in env_vars:
        if env_var not in os.environ:
            use_env_var = False

    g_config = {}
    # Global config objects
    if use_env_var is False:
        g_config = load_config(config_path)
    else:
        for env_var in env_vars:
            env_var_config = env_var.replace('PEP_', '')

            if "true" in os.environ[env_var].replace('"', ''):
                g_config[env_var_config.lower()] = True
            elif "false" in os.environ[env_var].replace('"', ''):
                g_config[env_var_config.lower()] = False
            else:
                g_config[env_var_config.lower()] = os.environ[env_var].replace(
                    '"', '')

    # Sanitize PDP "policy" endpoint config value, VERY IMPORTANT to ensure proper function of the endpoint
    if g_config["pdp_policy_endpoint"][0] is not "/":
        g_config["pdp_policy_endpoint"] = "/" + g_config["pdp_policy_endpoint"]
    if g_config["pdp_policy_endpoint"][-1] is not "/":
        g_config["pdp_policy_endpoint"] = g_config["pdp_policy_endpoint"] + "/"

    # Global handlers
    g_wkh = WellKnownHandler(g_config["auth_server_url"], secure=False)

    # Global setting to validate RPTs received at endpoints
    api_rpt_uma_validation = g_config["api_rpt_uma_validation"]
    if api_rpt_uma_validation:
        print("UMA RPT validation is ON.")
    else:
        print("UMA RPT validation is OFF.")

    # Generate client dynamically if one is not configured.
    if "client_id" not in g_config or "client_secret" not in g_config:
        print("NOTICE: Client not found, generating one... ")
        scim_client = EOEPCA_Scim(g_config["auth_server_url"])
        new_client = scim_client.registerClient(
            "PEP Dynamic Client",
            grantTypes=["client_credentials", "password"],
            redirectURIs=[""],
            logoutURI="",
            responseTypes=["code", "token", "id_token"],
            scopes=[
                'openid', 'uma_protection', 'permission', 'profile',
                'is_operator'
            ],
            token_endpoint_auth_method=ENDPOINT_AUTH_CLIENT_POST)
        print(
            "NEW CLIENT created with ID '" + new_client["client_id"] +
            "', since no client config was found on config.json or environment"
        )

        g_config["client_id"] = new_client["client_id"]
        g_config["client_secret"] = new_client["client_secret"]
        if use_env_var is False:
            save_config("config/config.json", g_config)
        else:
            os.environ["PEP_CLIENT_ID"] = new_client["client_id"]
            os.environ["PEP_CLIENT_SECRET"] = new_client["client_secret"]
        print("New client saved to config!")
    else:
        print("Client found in config, using: " + g_config["client_id"])

    save_config(config_path, g_config)

    return g_config, g_wkh
예제 #4
0
def main():
    logging.getLogger().setLevel(logging.INFO)

    #Determine Gluu host address
    gluuHost = "https://demoexample.gluu.org"

    #Initiate class
    scim_client = EOEPCA_Scim(host=gluuHost)

    #Initiate class with an existing user (INUM), specifying the location of their private RSA key file and kid (key ID) for UMA purposes
    #When no kid is provided, default value "RSA1" is used
    #scim_client = EOEPCA_Scim(host=gluuHost, clientID="", clientSecret="", jks_path="", kid="")

    #Register a new client, returns client information in JSON format
    clientName = "TestClient"
    grantTypes = [
        "client_credentials", "urn:ietf:params:oauth:grant-type:uma-ticket",
        "password", "implicit"
    ]
    redirectURIs = ["https://demoexample.gluu.org/web_ui/oauth/callback"]
    logoutURI = "https://demoexample.gluu.org/logout"
    responseTypes = []
    #Sector identifier is OPTIONAL field
    #Redirect URIs MUST be contained in Sector Identifier, if this is used
    sectorIdentifier = "https://demoexample.gluu.org/oxauth/sectoridentifier/9b473868-fa96-4fd1-a662-76e3663c9726"
    #sectorIdentifiers=None
    #OpenID scope examples
    scopes = ["openid", "oxd", "permission"]
    #UMA scope example
    #scopes=["https://demoexample.gluu.org/oxauth/restv1/uma/scopes/scim_access"]
    #Register call, with useJWK=1 if JWK is being used
    #clientJSON = scim_client.registerClient(clientName=clientName, grantTypes=grantTypes, redirectURIs=redirectURIs, logoutURI=logoutURI, responseTypes=responseTypes, scopes=scopes, token_endpoint_auth_method= ENDPOINT_AUTH_CLIENT_POST)
    clientJSON = scim_client.registerClient(
        clientName=clientName,
        grantTypes=grantTypes,
        redirectURIs=redirectURIs,
        logoutURI=logoutURI,
        responseTypes=responseTypes,
        scopes=scopes,
        sectorIdentifier=sectorIdentifier,
        token_endpoint_auth_method=ENDPOINT_AUTH_CLIENT_POST)
    #clientJSON = scim_client.registerClient(clientName=clientName, grantTypes=grantTypes, redirectURIs=redirectURIs, logoutURI=logoutURI, responseTypes=responseTypes, scopes=scopes, token_endpoint_auth_method= ENDPOINT_AUTH_CLIENT_PRIVATE_KEY_JWT , useJWK=1)
    print(clientJSON)

    #User to which we want to obtain all attributes
    userID = "*****@*****.**"

    #Get user attributes
    attributes = scim_client.getUserAttributes(userID=userID)
    print(attributes)

    #Add a new attribute
    attributePath = "name.middleName"
    newValue = "Middle"
    scim_client.addUserAttribute(userID=userID,
                                 attributePath=attributePath,
                                 newValue=newValue)

    #Modify an attribute
    attributePath = "name.familyName"
    newValue = "Last"
    scim_client.editUserAttribute(userID=userID,
                                  attributePath=attributePath,
                                  newValue=newValue)

    #Remove an attribute
    attributePath = "name.middleName"
    scim_client.removeUserAttribute(userID=userID, attributePath=attributePath)

    #Delete user
    reply = scim_client.deleteUser(userID=userID)
    print(reply)
예제 #5
0
class DemoClient:
    """Example client calling EOEPCA public endpoints
    """
    ROBOT_LIBRARY_SCOPE = 'GLOBAL'
    ROBOT_LIBRARY_VERSION = '0.1'

    def __init__(self, base_url):
        """Initialise client session with provided base URL.
        """
        self.base_url = base_url
        requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning)
        self.session = requests.Session()
        self.session.verify = False
        self.token_endpoint = None
        self.scim_client = None
        self.client = None
        self.load_state()
    
    def load_state(self):
        """Load state from file 'state.json'.
        """
        self.state = {}
        try:
            with open("state.json") as state_file:
                self.state = json.loads(state_file.read())
                print(f"State loaded from file: {self.state}")
        except FileNotFoundError:
            pass
        except json.decoder.JSONDecodeError:
            print(f"ERROR loading state from file. Using clean state...")
    
    @keyword(name='Client Save State')
    def save_state(self):
        """Save state to file 'state.json'.
        """
        state_filename = "state.json"
        with open(state_filename, "w") as state_file:
            state_file.write(json.dumps(self.state, sort_keys=True, indent=2))
            print("Client state saved to file:", state_filename)

    #---------------------------------------------------------------------------
    # USER MANAGEMENT
    #---------------------------------------------------------------------------

    def get_token_endpoint(self):
        """Get the URL of the token endpoint.

        Requires no authentication.
        """
        if self.token_endpoint == None:
            headers = { 'content-type': "application/json" }
            r = self.session.get(self.base_url + "/.well-known/uma2-configuration", headers=headers)
            self.token_endpoint = r.json()["token_endpoint"]
            print(f"token_endpoint: {self.token_endpoint}")
        return self.token_endpoint

    def register_client(self):
        """Register ourselves as a client of the platform.

        Skips registration if client is already registered (client_id/secret loaded from state file).
        """
        if not "client_id" in self.state:
            if self.scim_client == None:
                self.scim_client = EOEPCA_Scim(self.base_url + "/")
            self.client = self.scim_client.registerClient(
                "Demo Client",
                grantTypes = ["client_credentials", "password", "urn:ietf:params:oauth:grant-type:uma-ticket"],
                redirectURIs = [""],
                logoutURI = "",
                responseTypes = ["code","token","id_token"],
                scopes = ['openid',  'email', 'user_name ','uma_protection', 'permission'],
                token_endpoint_auth_method = ENDPOINT_AUTH_CLIENT_POST)
            if self.client["client_id"] and self.client["client_secret"]:
                self.state["client_id"] = self.client["client_id"]
                self.state["client_secret"] = self.client["client_secret"]
                print(f"client_id: {self.state['client_id']}")
            else:
                print("ERROR: Incomplete client credentials")
        else:
            print(f"client_id: {self.state['client_id']} [REUSED]")

    def get_client_credentials(self):
        """Returns the client credentials (client_id/secret)

        Performs client registration if needed.
        """
        if not "client_id" in self.state:
            self.register_client()
        return self.state["client_id"], self.state["client_secret"]

    @keyword(name='Get ID Token')
    def get_id_token(self, username, password):
        """Gets a user ID token using username/password authentication.
        """
        client_id, client_secret = self.get_client_credentials()
        headers = { 'cache-control': "no-cache" }
        data = {
            "scope": "openid user_name",
            "grant_type": "password",
            "username": username,
            "password": password,
            "client_id": client_id,
            "client_secret": client_secret
        }
        r = self.session.post(self.get_token_endpoint(), headers=headers, data=data)
        id_token = r.json()["id_token"]
        print(f"id_token: {id_token}")
        return id_token

    @keyword(name='Register Protected Resource')
    def register_protected_resource(self, service_url, uri, id_token, name, scopes):
        """Register a resource in the PEP

        Uses provided user ID token to authorise the request.
        Resource is identified by its path (URI).
        Resource is registered with the provided 'nice' name.
        """
        resource_id = None
        if "resources" in self.state:
            if service_url in self.state["resources"]:
                if uri in self.state["resources"][service_url]:
                    resource_id = self.state["resources"][service_url][uri]
            else:
                self.state["resources"][service_url] = {}
        else:
            self.state["resources"] = { service_url: {} }
        if resource_id == None:
            headers = { 'content-type': "application/json", "Authorization": f"Bearer {id_token}" }
            data = { "resource_scopes":scopes, "icon_uri":uri, "name":name}
            r = self.session.post(f"{service_url}/resources/{name}", headers=headers, json=data)
            resource_id = r.text
            if resource_id:
                self.state["resources"][service_url][uri] = resource_id
                print(f"resource_id: {resource_id} ({service_url}{uri})")
            else:
                print(f"ERROR: Empty resource ID for {service_url}{uri}")
        else:
            print(f"resource_id: {resource_id} ({service_url}{uri}) [REUSED]")
        return resource_id

    def get_access_token_from_ticket(self, ticket, id_token):
        """Convert UMA ticket to access token, using ID token for authentication.
        """
        if ticket == None or len(ticket) == 0 or id_token == None or len(id_token) == 0:
            print("ERROR: ticket and id_token are required")
            return
        client_id, client_secret = self.get_client_credentials()
        headers = { 'content-type': "application/x-www-form-urlencoded", "cache-control": "no-cache" }
        data = {
            "claim_token_format": "http://openid.net/specs/openid-connect-core-1_0.html#IDToken",
            "claim_token": id_token,
            "ticket": ticket,
            "grant_type": "urn:ietf:params:oauth:grant-type:uma-ticket",
            "client_id": client_id,
            "client_secret": client_secret,
            "scope": "openid"
        }
        r = self.session.post(self.get_token_endpoint(), headers=headers, data=data)
        access_token = r.json()["access_token"]
        print(f"access_token: {access_token}")
        return access_token

    def get_access_token_from_password(self, username, password):
        """Convert UMA ticket to access token, using username/password for authentication.
        """
        client_id, client_secret = self.get_client_credentials()
        headers = { 'content-type': "application/x-www-form-urlencoded", "cache-control": "no-cache" }
        data = {
            "grant_type": "password",
            "client_id": client_id,
            "client_secret": client_secret,
            "username": username,
            "password": password,
            "scope": "openid"
        }
        r = self.session.post(self.get_token_endpoint(), headers=headers, data=data)
        access_token = r.json()["access_token"]
        print(f"access_token: {access_token}")
        return access_token

    def uma_http_request(self, requestor, url, headers=None, id_token=None, access_token=None, json=None, data=None):
        """Helper to perform an http request via a UMA flow.

        Handles response code 401 to perform the UMA flow.
        The 'requestor' argument provides the function to be called to make the request, e.g. `requests.get`, `requests.post`, ...
        """
        # loop control variables
        count = 0
        repeat = True
        # max 2 loops. Repeat if we got a 401 and used UMA to get a new access token
        while repeat and count < 2:
            count += 1
            repeat = False
            # init headers if needed
            if headers is None:
                headers = {}
            # use access token if we have one
            if access_token is not None:
                headers["Authorization"] = f"Bearer {access_token}"
            # attempt access
            r = requestor(url, headers=headers, json=json, data=data)
            # if response is OK then nothing else to do
            if r.ok:
                pass
            # if we got a 401 then initiate the UMA flow
            elif r.status_code == 401:
                # need an id token for the UMA flow
                if id_token is not None:
                    # get ticket from the supplied header
                    location_header = r.headers["WWW-Authenticate"]
                    for item in location_header.split(","):
                        if item.split("=")[0] == "ticket":
                            ticket = item.split("=")[1]
                            break
                    # if we have a ticket then request an access token
                    if ticket is not None:
                        access_token = self.get_access_token_from_ticket(ticket, id_token)
                        repeat = True
            # unhandled response code
            else:
                print(f"UNEXPECTED status code: {r.status_code} for resource {url}")
        # return the response and the access token which may be reusable
        return r, access_token

    #---------------------------------------------------------------------------
    # ADES WPS
    #---------------------------------------------------------------------------

    @keyword(name='WPS Get Capabilities')
    def wps_get_capabilities(self, service_base_url, id_token=None, access_token=None):
        """Call the WPS GetCapabilities endpoint
        """
        url = service_base_url + "/?service=WPS&version=1.0.0&request=GetCapabilities"
        r, access_token = self.uma_http_request(self.session.get, url, id_token=id_token, access_token=access_token)
        print(f"[WPS Capabilities]=({r.status_code}-{r.reason})={r.text}")
        return r, access_token

    #---------------------------------------------------------------------------
    # ADES API PROCESSES
    #---------------------------------------------------------------------------

    @keyword(name='Proc List Processes')
    def proc_list_processes(self, service_base_url, id_token=None, access_token=None):
        """Call the 'API Processes' endpoint
        """
        url = service_base_url + "/processes"
        headers = { "Accept": "application/json" }
        r, access_token = self.uma_http_request(self.session.get, url, headers=headers, id_token=id_token, access_token=access_token)
        print(f"[Process List]=({r.status_code}-{r.reason})={r.text}")
        return r, access_token

    @keyword(name='Proc Deploy App')
    def proc_deploy_application(self, service_base_url, app_deploy_body_filename, id_token=None, access_token=None):
        """Deploy application via 'API Processes' endpoint

        The body of the deployment request is obtained from the supplied file
        """
        # get request body from file
        app_deploy_body = {}
        try:
            with open(app_deploy_body_filename) as app_deploy_body_file:
                app_deploy_body = json.loads(app_deploy_body_file.read())
                print(f"Application details read from file: {app_deploy_body_filename}")
        except FileNotFoundError:
            print(f"ERROR could not find application details file: {app_deploy_body_filename}")
        except json.decoder.JSONDecodeError:
            print(f"ERROR loading application details from file: {app_deploy_body_filename}")
        # make request
        url = service_base_url + "/processes"
        headers = { "Accept": "application/json", "Content-Type": "application/json" }
        r, access_token = self.uma_http_request(self.session.post, url, headers=headers, id_token=id_token, access_token=access_token, json=app_deploy_body)
        print(f"[Deploy Response]=({r.status_code}-{r.reason})={r.text}")
        return r, access_token

    @keyword(name='Proc App Details')
    def proc_get_app_details(self, service_base_url, app_name, id_token=None, access_token=None):
        """Get details for the application with the supplied name
        """
        url = service_base_url + "/processes/" + app_name
        headers = { "Accept": "application/json" }
        r, access_token = self.uma_http_request(self.session.get, url, headers=headers, id_token=id_token, access_token=access_token)
        print(f"[App Details]=({r.status_code}-{r.reason})={r.text}")
        return r, access_token

    @keyword(name='Proc Execute App')
    def proc_execute_application(self, service_base_url, app_name, app_execute_body_filename, id_token=None, access_token=None):
        """Execute application via 'API Processes' endpoint

        The body of the execute request is obtained from the supplied file
        """
        # get request body from file
        app_execute_body = {}
        try:
            with open(app_execute_body_filename) as app_execute_body_file:
                app_execute_body = json.loads(app_execute_body_file.read())
                print(f"Application execute details read from file: {app_execute_body_filename}")
        except FileNotFoundError:
            print(f"ERROR could not find application execute details file: {app_execute_body_filename}")
        except json.decoder.JSONDecodeError:
            print(f"ERROR loading application execute details from file: {app_execute_body_filename}")
        # make request
        url = service_base_url + "/processes/" + app_name + "/jobs"
        headers = { "Accept": "application/json", "Content-Type": "application/json", "Prefer": "respond-async" }
        r, access_token = self.uma_http_request(self.session.post, url, headers=headers, id_token=id_token, access_token=access_token, json=app_execute_body)
        job_location = r.headers['Location']
        print(f"[Execute Response]=({r.status_code}-{r.reason})=> job={job_location}")
        return r, access_token, job_location

    @keyword(name='Proc Job Status')
    def proc_get_job_status(self, service_base_url, job_location, id_token=None, access_token=None):
        """Get the job status from the supplied location
        """
        url = service_base_url + job_location
        headers = { "Accept": "application/json" }
        r, access_token = self.uma_http_request(self.session.get, url, headers=headers, id_token=id_token, access_token=access_token)
        print(f"[Job Status]=({r.status_code}-{r.reason})={r.text}")
        return r, access_token

    def proc_get_job_result(self, service_base_url, job_location, id_token=None, access_token=None):
        """Get the job result from the supplied location
        """
        url = service_base_url + job_location + "/result"
        headers = { "Accept": "application/json" }
        r, access_token = self.uma_http_request(self.session.get, url, headers=headers, id_token=id_token, access_token=access_token)
        print(f"[Job Result]=({r.status_code}-{r.reason})={r.text}")
        return r, access_token

    @keyword(name='Proc Undeploy App')
    def proc_undeploy_application(self, service_base_url, app_name, id_token=None, access_token=None):
        """Undeploy application via 'API Processes' endpoint
        """
        # make request
        url = service_base_url + "/processes/" + app_name
        headers = { "Accept": "application/json" }
        r, access_token = self.uma_http_request(self.session.delete, url, headers=headers, id_token=id_token, access_token=access_token)
        print(f"[Undeploy Response]=({r.status_code}-{r.reason})={r.text}")
        return r, access_token
예제 #6
0
        elif "false" in os.environ[env_var].replace('"', ''):
            g_config[env_var_config.lower()] = False
        else:
            g_config[env_var_config.lower()] = os.environ[env_var].replace('"', '')

# Global handlers
g_wkh = WellKnownHandler(g_config["auth_server_url"], secure=False)

# Generate client dynamically if one is not configured.
if "client_id" not in g_config or "client_secret" not in g_config:
    print ("NOTICE: Client not found, generating one... ")
    scim_client = EOEPCA_Scim(g_config["auth_server_url"])
    new_client = scim_client.registerClient("PEP Dynamic Client",
                                grantTypes = ["client_credentials"],
                                redirectURIs = [""],
                                logoutURI = "", 
                                responseTypes = ["code","token","id_token"],
                                scopes = ['openid', 'uma_protection', 'permission'],
                                token_endpoint_auth_method = ENDPOINT_AUTH_CLIENT_POST)
    print("NEW CLIENT created with ID '"+new_client["client_id"]+"', since no client config was found on config.json or environment")

    g_config["client_id"] = new_client["client_id"]
    g_config["client_secret"] = new_client["client_secret"]
    if use_env_var is False:
        save_config("config/config.json", g_config)
    else:
        os.environ["PEP_CLIENT_ID"] = new_client["client_id"]
        os.environ["PEP_CLIENT_SECRET"] = new_client["client_secret"]
    print("New client saved to config!")
else:
    print("Client found in config, using: "+g_config["client_id"])