Accessing EDG APIs with different types of authentication

In this section we will demonstrate how to call EDG Web APIs using Python for three different forms of authentication supported by EDG. For it, you will require a basic understanding of Python. For each example, we provide Python code to execute the EDG Web Service “exportRDFFile”. For more on using the exportRDFFile via Swagger and cURL, see Downloading an asset collection as RDF.

1. Using Basic authentication

Basic authentication relies on a Base64 encoded Authorization header whose value consists of the word Basic followed by a space followed by the Base64 encoded name:password. This is better suited for service-only access because the only way a user can log out is to shut down their browser. The URL with header information can be submitted with authentication information.

Here’s an example using Python to access the EDG exportRDFFile Web Service.

 1import argparse
 2import requests
 3import base64
 4import getpass
 5
 6
 7def main():
 8    parser = argparse.ArgumentParser(
 9        description="Make a GET request to a specified URL with credentials."
10    )
11    parser.add_argument("--username", required=True, help="Username for authentication")
12    parser.add_argument("--host", required=True, help="Hostname or IP address")
13    parser.add_argument(
14        "--asset_collection", required=True, help="Asset collection name"
15    )
16    parser.add_argument("--format", required=True, help="Format of export")
17    args = parser.parse_args()
18
19    # Prompt for password securely
20    password = getpass.getpass(prompt="Password: ")
21
22    # Encode username and password as base64
23    credentials = base64.b64encode(f"{args.username}:{password}".encode()).decode()
24
25    # Construct URL
26    url = f"http://{args.host}/service/{args.asset_collection}/tbs/exportRDFFile?format={args.format}"
27
28    try:
29        # Make GET request with Authorization header
30        response = requests.get(
31            url, headers={"Authorization": f"Basic {credentials}"}, verify=False
32        )
33
34        # Raise an HTTPError for bad responses
35        response.raise_for_status()
36
37        # Print the response text
38        print(response.text)
39
40    except requests.exceptions.HTTPError as http_err:
41        print(f"HTTP error occurred: {http_err}")
42    except requests.exceptions.RequestException as req_err:
43        print(f"Request error occurred: {req_err}")
44
45
46if __name__ == "__main__":
47    main()

To run the service use the following command -

python exportRDFFile.py --username <username>  --host <localhost> --asset_collection <asset_collection> --format <format>

When it prompts, enter your password. For example, running on an EDG instance locally with basic authentication, e.g. username admin, use the following command: python exportRDFFile_basic_auth.py --username "admin" --host "localhost:8083/tbl" --asset_collection "kennedy_family" --format "turtle". Then when prompted type in your password. On succesfully executing the script, it should print the kennedy_family ttl to the terminal/command window.

For secure access, web services should use HTTPS encryption.

2. Using Form-based authentication

Access using Form-based authentication, while not recommended, is possible using cookies generated by the server. The method we give here, using the following Python script, is to request an HTTP cookie that can be used in subsequent requests. The python script expects the same arguments as for basic auth, i.e. username, host, the asset collection name and the format. The inputs for these would be the same as for basic auth, except for host, which if you are running EDG studio locally with form based authentication would be "http://localhost:8083/tbl". You enter your password again when you receive a prompt.

  1. The main function specifies the required arguments, and then proceeds to create a session to handle the cookies. Next it executes the login function using this session.

  2. The login function takes the host URL and performs an initial POST request to obtain a cookie from the server.

  3. The login script then performs a second POST request to login, now making use of the username and password as parameters.

  4. On successful login, the python script uses the session to run subsequent http request. In this example we once again call the EDG exportRDFFile web service.

 1import argparse
 2import requests
 3import sys
 4import getpass
 5
 6
 7def login(session, input_url, username, password):
 8    try:
 9        # Perform the initial POST request to obtain cookies
10        response = session.post(input_url)
11
12        # Check if the request was successful
13        response.raise_for_status()
14
15        # Perform the second POST request with cookies
16        login_data = {"j_username": username, "j_password": password}
17
18        login_headers = {"User-Agent": "Mozilla/5.0"}
19
20        login_url = input_url + "/swp?_viewName=home"
21        response = session.post(
22            login_url, data=login_data, headers=login_headers, allow_redirects=False
23        )
24
25        # Perform the third POST request with cookies to complete the login
26        j_security_check_url = input_url + "/j_security_check"
27
28        response = session.post(
29            j_security_check_url, data=login_data, headers=login_headers
30        )
31
32        # Check if the login was successful
33        response.raise_for_status()
34
35    except requests.exceptions.RequestException as e:
36        print(f"Error during login: {e}")
37        sys.exit(1)
38
39
40def main():
41    parser = argparse.ArgumentParser(
42        description="Make requests with login to a specified URL."
43    )
44    parser.add_argument("--username", required=True, help="Username for authentication")
45    parser.add_argument("--host", required=True, help="Input URL")
46    parser.add_argument(
47        "--asset_collection", required=True, help="Asset collection name"
48    )
49    parser.add_argument("--format", required=True, help="Export format")
50    args = parser.parse_args()
51
52    try:
53        # Prompt for password securely
54        password = getpass.getpass(prompt="Password: ")
55
56        # Create a session to handle cookies
57        session = requests.Session()
58
59        # Perform login
60        login(session, args.host, args.username, password)
61
62        # Now you can use the session for subsequent requests
63
64        # Perform the GET request using success cookies
65        base_url = f"{args.host}/service/{args.asset_collection}/tbs/exportRDFFile?format={args.format}"
66        headers = {"accept": "application/json"}
67
68        response = session.get(base_url, headers=headers)
69
70        if response.status_code == 200:
71            print("Request was successful")
72            print(response.text)
73        else:
74            print(f"Request failed with status code {response.status_code}")
75            print(response.text)
76
77    except requests.exceptions.RequestException as e:
78        print(f"Error making the request: {e}")
79        sys.exit(1)
80
81
82if __name__ == "__main__":
83    main()

Use the following command to execute the script python exportRDFFile_basic_auth.py --username "admin" --host "http://localhost:8083/tbl" --asset_collection "kennedy_family" --format "turtle". Then again, type in your password.

On successfully executing the script, it should print the kennedy_family ttl to the terminal/command window.

For secure access, web services should use HTTPS encryption.

3. Using OAuth authentication

The following piece of Python script provides a means for calling the exportRDFFile when using OAuth authentication. The code takes the following parameters; host, the asset collection name and the format. The authentication server, client id and scope could also be potentially passed in as parameters. Here, we hard code them. We also prompt the use for their client secret. Again, this could be passed as a parameter.

The code is broken into a main function and login function. The login function prompts the user for their client secret. This is then used to generate the “access_token”, which is returned by the function.

The main function then takes the three parameters, provides hard coded auth_server_url and client_id. Here we assemble the API URL from the parameters we pass in, but you can replace this with any API URL. The code then generates the access token hourly, prompting each time for the client secret.

On successful execution, it will write the response data to the terminal/command line.

 1#!/usr/local/bin/python3
 2#
 3"""
 4Sample API client using OAuth 2 client credentials flow.  It will will repeat
 5an API call every hour and generate a new token as needed.
 6
 71. Call API
 82. If access token is expired, use the client_secret to get a new one
 93. Repeat
10
11@author: TopQuadrant
12"""
13import argparse
14import getpass
15import json
16import time
17import urllib.parse
18import urllib.request
19import ssl
20
21
22def authenticate(url, client_id):
23    """
24    Authenticates the client and generates a new access token.
25    """
26    client_secret = getpass.getpass("Client secret: ")
27    request_data = {
28        "client_id": client_id,
29        "client_secret": client_secret,
30        "grant_type": "client_credentials",
31        "scope": "api://uuid/.default",  # add your scope here
32    }
33    encoded_data = urllib.parse.urlencode(request_data)
34    binary_data = encoded_data.encode("ascii")
35    request = urllib.request.Request(url)
36    print("Getting token...")
37    response = urllib.request.urlopen(request, binary_data)
38    # Just to be safe...
39    client_secret = None
40    tokens = json.load(response)
41    access_token = tokens["access_token"]
42    return access_token
43
44
45def main():
46    parser = argparse.ArgumentParser(
47        description="Make requests with login to a specified URL."
48    )
49
50    parser.add_argument("--host", required=True, help="Input URL")
51    parser.add_argument(
52        "--asset_collection", required=True, help="Asset collection name"
53    )
54    parser.add_argument("--format", required=True, help="Export format")
55    args = parser.parse_args()
56
57    # Copy your authorization server URL and paste it here
58    auth_server_url = "https://your.adfs.server/adfs/oauth2/token"
59    # Copy your client ID and paste it here
60    client_id = "12345678-90ab-cdef-1234-567890abcdef"
61    # Copy your EDG API URL and paste it here
62    api_url = f"https://{args.host}/edg/tbl/service/{args.asset_collection}/tbs/exportRDFFile?format={args.format}"
63    # Copy your initial access_token you got from the authorization server and paste it here
64    access_token = ""
65
66    # Run the API call once per hour
67    print("Running hourly process...")
68    while True:
69        # What time is it?
70        print(time.strftime("%c"))
71        # Use the access token
72        auth_header = {"Authorization": "Bearer " + access_token}
73        # Make a request
74        request = urllib.request.Request(api_url, None, auth_header)
75        try:
76            response = urllib.request.urlopen(request)
77            print("Response status was " + str(response.getcode()))
78        except urllib.error.HTTPError as error:
79            # Check status code
80            if error.code == 401:
81                # Need to use client_secret to get a new token
82                access_token = authenticate(auth_server_url, client_id)
83                # Use the new access token
84                auth_header = {"Authorization": "Bearer " + access_token}
85                # Repeat the request
86                request = urllib.request.Request(api_url, None, auth_header)
87                # Get the response
88                response = urllib.request.urlopen(request)
89                print("Response status was " + str(response.getcode()))
90        # Do something useful with the response.  (Your biz logic goes here)
91        result = print(response.read())
92        #
93        # Wait until next time
94        print("Sleeping.")
95        time.sleep(3600)
96
97
98if __name__ == "__main__":
99    main()

Use the following command to execute the script python exportRDFFile_oauth_auth.py --username --host "localhost:8083" --asset_collection "kennedy_family" --format "turtle"