diff --git a/build/docker-compose.yaml b/build/docker-compose.yaml deleted file mode 100644 index 0d307a2..0000000 --- a/build/docker-compose.yaml +++ /dev/null @@ -1,47 +0,0 @@ -version: "3.9" -services: - coder: - # This MUST be stable for our documentation and - # other automations. - image: ghcr.io/coder/coder:${CODER_VERSION:-latest} - ports: - - "7080:7080" - environment: - CODER_PG_CONNECTION_URL: "postgresql://${POSTGRES_USER:-username}:${POSTGRES_PASSWORD:-password}@database/${POSTGRES_DB:-coder}?sslmode=disable" - CODER_HTTP_ADDRESS: "0.0.0.0:7080" - # You'll need to set CODER_ACCESS_URL to an IP or domain - # that workspaces can reach. This cannot be localhost - # or 127.0.0.1 for non-Docker templates! - CODER_ACCESS_URL: "${CODER_ACCESS_URL}" - # If the coder user does not have write permissions on - # the docker socket, you can uncomment the following - # lines and set the group ID to one that has write - # permissions on the docker socket. - #group_add: - # - "998" # docker group on host - volumes: - - /var/run/docker.sock:/var/run/docker.sock - depends_on: - database: - condition: service_healthy - database: - image: "postgres:14.2" - ports: - - "5432:5432" - environment: - POSTGRES_USER: ${POSTGRES_USER:-username} # The PostgreSQL user (useful to connect to the database) - POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-password} # The PostgreSQL password (useful to connect to the database) - POSTGRES_DB: ${POSTGRES_DB:-coder} # The PostgreSQL default database (automatically created at first launch) - volumes: - - coder_data:/var/lib/postgresql/data # Use "docker volume rm coder_coder_data" to reset Coder - healthcheck: - test: - [ - "CMD-SHELL", - "pg_isready -U ${POSTGRES_USER:-username} -d ${POSTGRES_DB:-coder}", - ] - interval: 5s - timeout: 5s - retries: 5 -volumes: - coder_data: \ No newline at end of file diff --git a/python/akamai_functions.py b/python/akamai_functions.py deleted file mode 100644 index f15edb4..0000000 --- a/python/akamai_functions.py +++ /dev/null @@ -1,551 +0,0 @@ -from akamai.edgegrid import EdgeGridAuth -from urllib.parse import urljoin -from datetime import datetime, timedelta -import requests, json -import re -import time -import names -from flask import current_app - -# Control Center Account is 'Marketplace Test01' -contract_id = 'ctr_V-41DUHPB' - -# admin user API credential -client_secret = 'HP299+k2YnnyRKtbzKYtGOrM4ve1tXlrsn6o3ZZ/Mdw=' -host = 'akab-hkgndwdao42uuh4y-q4itpussnn3gck4x.luna.akamaiapis.net' -access_token = 'akab-oaav5wopp546rowg-gjkaonxqkb6suzo7' -client_token = 'akab-4daj4uu4qpqiukly-5kbvowcutmvydqk2' -baseurl = 'https://' + host - -# unused hours of user -unused_hours = 5 - -sess = requests.Session() -sess.auth = EdgeGridAuth( - client_token=client_token, - client_secret=client_secret, - access_token=access_token -) - - -def get_all_users(): - response = sess.get(urljoin( - baseurl, '/identity-management/v3/user-admin/ui-identities?authGrants=true')) - s_code = response.status_code - body = response.json() - users = body - return users - -def verify_user_assigned_ever(user, firstName, lastName): - is_user_assigned_ever = False - if user['firstName'] == firstName and user['lastName'] == lastName: - is_user_assigned_ever = True - return is_user_assigned_ever - -def verify_user_email_domain(user): - pattern = r'^[a-zA-Z0-9_.+-]+@akamai-lab\.com$' - is_correct_domain = False - - email = user['email'] - if re.fullmatch(pattern, email): - is_correct_domain = True - return is_correct_domain - -def verify_user_lastLoginDate(user, hours): - is_valid = False - if 'lastLoginDate' in user: # if user has ever logged in to Control Center - date = user['lastLoginDate'] - loginDate = datetime.strptime(date, "%Y-%m-%dT%H:%M:%S.%fZ") - # current_app.logger.debug('lastLoginDate='+date) - currentDate = datetime.now() - # current_app.logger.debug('now='+str(currentDate)) - difference = (currentDate - loginDate).total_seconds() / 3600 - difference = int(difference) - # current_app.logger.debug('hoursSinceLastLogin='+str(difference)+' hours') - if difference > hours: # if user has not logged in longer than unusedDays - is_valid = True - else: # user has never logged in to Control Center yet - is_valid = True - return is_valid - -def select_user(users, hours, firstName, lastName): - selectedUser = None - current_app.logger.debug('hours='+str(hours)) - - # To avoid user pool exhaustion, we reuse existing users if they remain in the pool. - for user in users: - if verify_user_assigned_ever(user, firstName, lastName): - if verify_user_lastLoginDate(user, hours) is False: - selectedUser = user - break - # if there is no existing user in the pool, we search for a random available user - if selectedUser == None: - for user in users: - if verify_user_email_domain(user) and verify_user_lastLoginDate(user, hours): - uiIdentityId = user['uiIdentityId'] - selectedUser = update_user(uiIdentityId, firstName, lastName) - break - - if selectedUser != None: - current_app.logger.debug('selectedUser= %s', selectedUser) - else: - raise Exception('cannot find an available user') - # we might want to create a new group and a user here??? - return selectedUser - -def reset_user_pwd(u_id): - response = sess.post(urljoin(baseurl, '/identity-management/v3/user-admin/ui-identities/' + - u_id+'/reset-password?sendEmail=false')) - pwd = response.json()['newPassword'] - return pwd - -def update_user(uiIdentityId, firstName, lastName): - requestBody = { - "firstName": "John", - "lastName": "Doe", - "country": "USA", - "phone": "3456788765", - "contactType": "Billing", - "preferredLanguage": "English", - "sessionTimeOut": 64800, - "timeZone": "GMT" - } - - current_app.logger.debug('uiIdentityId= %s', uiIdentityId) - requestBody['firstName'] = firstName - requestBody['lastName'] = lastName - current_app.logger.debug('requestBody= %s', requestBody) - - response = sess.put(urljoin(baseurl, '/identity-management/v3/user-admin/ui-identities/' + - uiIdentityId + '/basic-info'), json=requestBody) - s_code = response.status_code - current_app.logger.debug('update_user status code= %s', s_code) - body = response.json() - current_app.logger.debug('update_user response body= %s', body) - - return body - -def find_credential(authorizedUser, file_path): - credential =[] - file = open(file_path) - clients = json.load(file) - current_app.logger.debug(f'clients= {clients}') - for client in clients: - user = client["authorizedUser"] - if user == authorizedUser: - credential = client["credentials"] - break - - if len(credential) > 0 : - print(f'credentials= {credential}') - return credential - else: - return None - -def create_credential(credential): - # this credential belongs to the selected user - client_secret = credential["client_secret"] - host = credential["host"] - access_token = credential["access_token"] - client_token = credential["client_token"] - baseurl = "https://" + host - - new_credential={ - "client_secret": "", - "host": "", - "access_token": "", - "client_token": "" - } - new_credential["host"] = host - new_credential["access_token"] = access_token - - sess = requests.Session() - sess.auth = EdgeGridAuth( - client_token=client_token, - client_secret=client_secret, - access_token=access_token - ) - headers = { "Accept":"application/json"} - - # create a new credential for the selected user - response = sess.post(urljoin(baseurl, "/identity-management/v3/api-clients/self/credentials"), headers=headers) - s_code = response.status_code - current_app.logger.debug(f"status code= {s_code}") - if s_code == 201: - body = response.json() - current_app.logger.debug(f"response body= {body}") - new_credential["client_secret"] = body["clientSecret"] - new_credential["client_token"] = body["clientToken"] - credentialId = body["credentialId"] - current_app.logger.info(f"new credentialId= {credentialId}") - - # if a new credential is created, start updating its expiration - payload = { - "expiresOn": "2020-10-11T23:06:59.000Z", - "status": "ACTIVE", - "description": "Expiration 4 hours" - } - - current = datetime.now() - current_app.logger.debug (f"current= {current}") - expiration = current + timedelta(hours = 4) - current_app.logger.debug(f"expiration= {expiration}") - payload["expiresOn"] = expiration.strftime("%Y-%m-%dT%H:%M:%S.%fZ") - current_app.logger.debug(f"payload= {payload}") - - path = "/identity-management/v3/api-clients/self/credentials/{}".format(credentialId) - headers = { - "Content-Type": "application/json", - "Accept": "application/json"} - - # update the new credential expiration - response = sess.put(urljoin(baseurl, path), headers=headers, json=payload) - s_code = response.status_code - current_app.logger.debug(f"status code= {s_code}") - - if s_code == 200: - body = response.json() - current_app.logger.debug(f"update credential response= {body}") - return new_credential - else: - return None - else: - return None - -######################################################### -# The following functions are for administrator only!!! # -######################################################### - -def is_user_in_users(users, uiUserName): - is_user_in_users = False - users = get_all_users() - for user in users: - if user['uiUserName'] == uiUserName: - is_user_in_users = True - current_app.logger.debug('user %s exists in all users', uiUserName) - break - if is_user_in_users == False: - current_app.logger.debug( - 'cannot find user %s in all users', uiUserName) - return is_user_in_users - -######################################################### -# functions for deleting old properties # -######################################################### - - -def get_properties_by_group(group_id): - if isinstance(group_id, str) == False: - group_id = str(group_id) - - response = sess.get(urljoin( - baseurl, '/papi/v1/properties?contractId='+contract_id+'&groupId='+group_id)) - properties = response.json() - return properties - -# this function is used by delete_properties(properties) -def get_property_details(property_id, contract_id, group_id): - response = sess.get(urljoin(baseurl, '/papi/v1/properties/' + - property_id+'?contractId='+contract_id+'&groupId='+group_id)) - s_code = response.status_code - property_details = response.json() - if s_code != 200: - current_app.logger.debug( - 'failed to check status of property '+property_id) - current_app.logger.debug(property_details) - else: - current_app.logger.debug('checked status of property '+property_id) - return property_details - - -def deactivate_property(property_id, network, property_version): - deactivate_payload = { - "acknowledgeAllWarnings": False, - "activationType": "DEACTIVATE", - "fastPush": True, - "ignoreHttpErrors": True, - "notifyEmails": ["learn@akamai.com"], - "useFastFallback": False, - "network": "STAGING", - "propertyVersion": 0 - } - activation_link = None - if network == 'STAGING': - deactivate_payload['network'] = 'STAGING' - elif network == 'PRODUCTION': - deactivate_payload['network'] = 'PRODUCTION' - else: - current_app.abort( - 500, description="deactivation target network value is invalid >> network= "+network) - - current_app.logger.debug('deactivation target network is ' + - str(deactivate_payload['network'])) - deactivate_payload['propertyVersion'] = property_version - current_app.logger.debug('property_version= '+str(deactivate_payload['propertyVersion']) - ) - response = sess.post(urljoin(baseurl, '/papi/v1/properties/' + - property_id+'/activations'), json=deactivate_payload) - s_code = response.status_code - body = response.json() - current_app.logger.debug('deactivation response= '+str(body)) - activation_link = str(body['activationLink']) - if s_code != 201: - current_app.logger.debug('failed to deactivate property '+property_id) - current_app.logger.debug(body) - else: - current_app.logger.debug('started deactivation of property ' + - property_id + ' with activationLink '+activation_link) - return activation_link - - -def delete_properties(properties): - deleted = False - - staging_version = None - production_version = None - - if len(properties['properties']['items']) > 0: - for property in properties['properties']['items']: - current_app.logger.debug('property= ' + str(property)) - property_id = property['propertyId'] - group_id = property['groupId'] - current_app.logger.debug('property_id= '+property_id) - current_app.logger.debug('contract_id= '+contract_id) - current_app.logger.debug('group_id= '+group_id) - - # Check property activation status - property_details = get_property_details( - property_id, contract_id, group_id) - current_app.logger.debug( - 'property_details= '+str(property_details)) - - # Deactivate property if it is active in staging or network - staging_version = property_details['properties']['items'][0]['stagingVersion'] - if staging_version != 'None' and isinstance(staging_version, int): - current_app.logger.debug('property '+property_id+' version ' + - str(staging_version)+' is active in staging network') - activation_link = deactivate_property( - property_id, 'STAGING', staging_version) - if activation_link != None: - current_app.logger.info( - 'deactivation started in staging network. we will wait until it finishes') - while True: - time.sleep(1) - response = sess.get(urljoin(baseurl, activation_link)) - s_code = response.status_code - body = response.json() - current_app.logger.debug( - 'get activation response code= '+str(s_code)) - current_app.logger.debug( - 'get activation response body= '+str(body)) - - status = str(body['activations']['items'][0]['status']) - activation_id = str( - response['activations']['items'][0]['activationId']) - current_app.logger.debug('activation_id ' + - activation_id+' is '+status) - if status == 'DEACTIVATED': - current_app.logger.info( - 'finished deactivation of property '+property_id + ' in staging network') - break - else: - current_app.logger.debug( - 'deactivation is in progress. We will check activation status 1 second later') - else: - current_app.logger.debug('property '+property_id + - ' is not active in staging network') - - # Deactivate property if it is active in production or network - production_version = property_details['properties']['items'][0]['productionVersion'] - if production_version != 'None' and isinstance(production_version, int): - current_app.logger.debug('property '+property_id+' version ' + - str(production_version)+' is active in production network') - activation_link = deactivate_property( - property_id, 'PRODUCTION', production_version) - if activation_link != None: - current_app.logger.info( - 'deactivation started in production network. we will wait until it finishes') - while True: - time.sleep(1) - response = sess.get(urljoin(baseurl, activation_link)) - body = response.json() - current_app.logger.debug( - 'get activation response code= '+str(s_code)) - current_app.logger.debug( - 'get activation response body= '+str(body)) - status = str(body['activations']['items'][0]['status']) - activation_id = str( - body['activations']['items'][0]['activationId']) - current_app.logger.debug('activation_id ' + - activation_id+' is '+status) - if status == 'DEACTIVATED': - current_app.logger.info( - 'finished deactivation of property '+property_id) - break - else: - current_app.logger.debug( - 'deactivation is in progress. We will check activation status again 1 second later') - else: - current_app.logger.debug('property '+property_id + - ' is not active in production network') - - # Delete properties - response = sess.delete( - urljoin(baseurl, '/papi/v1/properties/'+property_id)) - s_code = response.status_code - body = response.json() - current_app.logger.debug('delete status code= '+str(s_code)) - if s_code != 200: - current_app.logger.debug( - 'failed to delete property '+property_id) - current_app.logger.debug(body) - deleted = False - break - else: - deleted = True - current_app.logger.debug('deleted property '+property_id) - - else: - current_app.logger.debug('cannot find any property') - - return deleted - -###################################################### -# functions for creating groups and users # -###################################################### - - -def create_users(num): - new_users = [] - for i in range(num): - group = create_group() - groupId = group['groupId'] - groupName = group['groupName'] - uiUserName = groupName + '@akamai-lab.com' - user = create_user(groupId, groupName) - if user != None: - current_app.logger.debug('user '+uiUserName+' is created') - new_users.append(user) - else: - current_app.logger.debug('cannot create a new user') - if len(new_users) > 0: - return new_users - else: - return None - - -def create_group(): - parent_groupId = '232397' - groupName = str(datetime.now().strftime('%Y%m%d%H%M%S')) - current_app.logger.debug('groupName= %s', groupName) - requestBody = {"groupName": groupName} - response = sess.post(urljoin( - baseurl, '/identity-management/v3/user-admin/groups/'+parent_groupId), json=requestBody) - s_code = response.status_code - current_app.logger.debug( - 'create_group status code= '+str(s_code)) - body = response.json() - current_app.logger.debug('create_group response body= %s', body) - groupId = body['groupId'] - - if s_code == 201: - return body - else: - return 'cannot create a new group ' + str(groupName) - - -def create_user(groupId, groupName): - headers = {"accept": "application/json"} - requestBody = { - "authGrants": [ - { - # 3 = admin, 928 = editor. refer to 'list_roles.json' for more information. - "roleId": 928, - "groupId": 12345 - } - ], - "firstName": "John", - "lastName": "Doe", - "email": "@akamai-lab.com", - "phone": "(123) 321-1234", - "additionalAuthentication": "NONE", - "country": "USA" - } - - # firstName = names.get_first_name() - # lastName = names.get_last_name() - firstName = 'Not' - lastName = 'Assigned' - email = groupName + '@akamai-lab.com' - current_app.logger.debug('email= '+email) - requestBody['firstName'] = firstName - requestBody['lastName'] = lastName - requestBody['email'] = email - requestBody['authGrants'][0]['groupId'] = groupId - current_app.logger.debug('create_user request body= %s', requestBody) - - response = sess.post(urljoin( - baseurl, '/identity-management/v3/user-admin/ui-identities?sendEmail=false'), json=requestBody, headers=headers) - s_code = response.status_code - current_app.logger.debug('create_user status code= %s', s_code) - body = response.json() - current_app.logger.debug('create_user response body= %s', body) - - if s_code == 201: - return body - else: - return None - -####################################################### - - -def list_roles(): - response = sess.get(urljoin( - baseurl, '/identity-management/v3/user-admin/roles')) - s_code = response.status_code - current_app.logger.debug('list_roles status code= %s', s_code) - body = response.json() - current_app.logger.debug('list_roles response body= %s', body) - - if s_code == 200: - return body - else: - return 'cannot list roles' - - -def get_groups(unused_hours): - pattern = r'^[a-zA-Z0-9_.+-]+@akamai-lab\.com$' - current_app.logger.debug('unused_hours='+str(unused_hours)) - - groups = [] - users = get_all_users() - filtered_users = [{}] - for user in users: - # current_app.logger.debug('user= ' + str(user)) - email = user['email'] - current_app.logger.debug('email= '+email) - is_correct_user = False - if re.fullmatch(pattern, email): # if email domain is akamai-lab.com - if 'lastLoginDate' in user: # if the user has ever logged in to Control Center - date = user['lastLoginDate'] - loginDate = datetime.strptime(date, "%Y-%m-%dT%H:%M:%S.%fZ") - current_app.logger.debug('lastLoginDate='+date) - currentDate = datetime.now() - current_app.logger.debug('now='+str(currentDate)) - difference = (currentDate - loginDate).total_seconds() / 3600 - current_app.logger.debug( - 'hoursSinceLastLogin=' + str(difference)+'hour(s)') - if difference > unused_hours: # if the user has not logged in longer than unused hours - is_correct_user = True - else: # if the user has never logged in - is_correct_user = True - - if is_correct_user: - groupId = user['authGrants'][0]['groupId'] - groupName = user['authGrants'][0]['groupName'] - group = {"groupName": "", "groupId": ""} - group['groupName'] = groupName - group['groupId'] = groupId - groups.append(group) - - return groups \ No newline at end of file diff --git a/python/app.py b/python/app.py deleted file mode 100644 index e83db5f..0000000 --- a/python/app.py +++ /dev/null @@ -1,337 +0,0 @@ -from flask import Flask, request, make_response, abort, jsonify, render_template, send_file -from akamai.edgegrid import EdgeGridAuth -from urllib.parse import urljoin -from datetime import datetime, timedelta -import requests -import time -import subprocess -import logging -import re -import json -import akamai_functions, docebo_functions, coder_functions -from threading import Thread -import randomname -from io import StringIO -import os - -app = Flask(__name__) -app.logger.setLevel(logging.DEBUG) - -# How can we update coder admin_token??? What is the maximum expiration days? -# coder server --max-token-lifetime. Default 2540400 hours = 290 days -# The maximum lifetime duration users can specify when creating an API token. -# coder tokens create --lifetime. Default 720 hours = 30 days -# Specify a duration for the lifetime of the token. -# Create token API key - curl -X POST http://coder-server:8080/api/v2/users/{user}/keys/tokens -# { -# "lifetime": 0, -# "scope": "all", -# "token_name": "string" -# } - -admin_token = 'gNmzL1TLeN-gXfs7q10uWPINqtlpt02Pj' -template_id = 'a5577a86-e700-41be-997b-6001d78061d4' -user_email = 'learn@akamai.com' -user_pwd = 'Qodnwk=$s8' - -token_header = {'Coder-Session-Token': admin_token, - 'Accept': 'application/json'} -token_param = {'email': user_email, 'password': user_pwd} - -# unused hours of workspace -unused_hours = 2 - -# workspace ttl ms -ttl = 86400000 # 24 hours - -################################################### -# Control Center API functions start # -################################################### - -@app.route('/flask/cc_download') -def cc_download(): - firstName = randomname.get_name() - lastName = randomname.get_name() - app.logger.debug('firstName= %s, lastName= %s', firstName, lastName) - - app.logger.debug('get_users starts') - users = akamai_functions.get_all_users() - app.logger.debug('get_users ends') - - app.logger.debug('select_user starts') - user = akamai_functions.select_user(users, unused_hours, firstName, lastName) - uiIdentityId = user['uiIdentityId'] - app.logger.debug('select_user ends. uiIdentityId= %s', uiIdentityId) - - app.logger.debug('reset_user_pwd starts') - pwd = akamai_functions.reset_user_pwd(uiIdentityId) - app.logger.debug('reset_user_pwd ends') - - authorizedUser = user['uiUserName'] - app.logger.debug(f'authorizedUser= {authorizedUser}') - credential = akamai_functions.find_credential(authorizedUser, '/home/akamai/dev/learnakamai/json/api_clients.json') - if credential is not None: - new_credential = akamai_functions.create_credential(credential) - if new_credential is not None: - credential = new_credential - else: - abort(500, description="cannot create new credential") - else: - abort(500, description="cannot find credential") - - email = user['email'] - if email != None and pwd != None: - authorizedUser = user['uiUserName'] - - app.logger.debug(f'credential= {credential}') - result = '[Control Center]\nLogin URL = https://control.akamai.com\n' + 'email = '+ email + '\npassword = '+ pwd + '\n\n' - result = result + '[API Credential]\nclient_secret = ' + credential["client_secret"] + '\nhost = '+ credential["host"] + '\naccess_token = ' + credential["access_token"] + '\nclient_token = ' + credential["client_token"] - app.logger.debug(f'result = '+result) - - - buffer = StringIO() - buffer.write(result) - - response = make_response(buffer.getvalue()) - response.headers['Content-Disposition'] = 'attachment; filename=credential.txt' - response.mimetype = 'application/octet-stream' - - # return send_file(buffer, as_attachment=True, download_name="credential.txt", mimetype="html/text") - - # response = make_response('-- API credential --
client_secret = '+ credential["client_secret"] + '
host = '+ credential["host"] + '
access_token = ' + credential["access_token"] + '
client_token = ' + credential["client_token"]) - return response - else: - abort(500, description="cannot find an available user") - return user - -# this function is the target url of iframe widget -@app.route('/flask/credential') -def credential(): - return render_template('playground.html') - -# this function is called by playground.html javascript -@app.route('/flask/a') -def a(): - docebo_user_id = request.args.get('user_id') - docebo_access_token = request.args.get('access_token') - - app.logger.debug('get_users starts') - users = akamai_functions.get_all_users() - app.logger.debug('get_users ends') - - app.logger.debug('docebo_get_users stars') - if docebo_user_id != None and docebo_access_token != None: - docebo_user = docebo_functions.docebo_get_user(docebo_user_id, docebo_access_token) - if docebo_user == None: - return 'cannot get Learn Akamai userdata of '+ docebo_user_id - else: - return 'docebo userdata is required' - firstName = docebo_user['first_name'] - lastName = docebo_user['last_name'] - app.logger.debug('docebo_get_users ends. firstName= %s, lastName= %s', firstName, lastName) - - app.logger.debug('select_user starts') - user = akamai_functions.select_user(users, unused_hours, firstName, lastName) - uiIdentityId = user['uiIdentityId'] - app.logger.debug('select_user ends. uiIdentityId= %s', uiIdentityId) - - app.logger.debug('reset_user_pwd starts') - pwd = akamai_functions.reset_user_pwd(uiIdentityId) - app.logger.debug('reset_user_pwd ends') - - authorizedUser = user['uiUserName'] - app.logger.debug(f'authorizedUser= {authorizedUser}') - credential = akamai_functions.find_credential(authorizedUser, '/home/akamai/dev/learnakamai/json/api_clients.json') - if credential is not None: - new_credential = akamai_functions.create_credential(credential) - if new_credential is not None: - credential = new_credential - else: - abort(500, description="cannot create new credential") - else: - abort(500, description="cannot find credential") - - email = user['email'] - if email != None and pwd != None: - authorizedUser = user['uiUserName'] - - app.logger.debug(f'credential= {credential}') - response = make_response('-- API credential --
client_secret = '+ credential["client_secret"] + '
host = '+ credential["host"] + '
access_token = ' + credential["access_token"] + '
client_token = ' + credential["client_token"]) - return response - else: - abort(500, description="cannot find an available user") - return user - -######################################################################################### -# this function deletes properties in groups that have not been used for unused_hours-1 # -# this function will be updated to run repeatedly # -######################################################################################### -@app.route('/flask/purge_groups') -def purge_groups(): - groups = akamai_functions.get_groups(unused_hours-1) - app.logger.debug('groups= '+str(groups)) - messages = [] - msg = None - for group in groups: - groupId = group['groupId'] - groupName = group['groupName'] - properties = akamai_functions.get_properties_by_group(groupId) - # if there is any existing properties in group - if properties['properties']['items']: - msg = 'found properties in group '+groupName - messages.append(msg) - app.logger.info(msg) - app.logger.debug('properties='+str(properties)) - - if akamai_functions.delete_properties(properties): - msg = 'deleted existing properties in group '+groupName - messages.append(msg) - app.logger.info(msg) - else: - abort(500, description="cannot delete properties in group "+groupName) - else: - msg = 'cannot find any property in group '+groupName - messages.append(msg) - app.logger.info(msg) - if bool(messages): - return messages - else: - abort(500, description="cannot purge groups") - -# test creating users -@app.route('/flask/c') -def c(): - num = int(request.args.get('num')) - if num == None: num = 1 - app.logger.debug('we will create %s user(s)', num) - new_users = akamai_functions.create_users(num) - if new_users != None: - return new_users - else: - return 'cannot create users' - -# Check list roles to assign users to right roles -@app.route('/flask/list_roles') -def d(): - result = akamai_functions.list_roles() - return result -################################################### -# Control Center API functions end # -################################################### - - - - - -################################################### -# coder API functions start # -################################################### - - -# this function is the target URL of iframe widget -@app.route('/flask/lab', methods=['GET']) -def lab(): - # return render_template('image_render.html', image=file) - return render_template('workspace.html') - -#@app.route('/flask/lab', methods=['GET']) -#def lab(): -# user_id = request.args.get('user_id') -# access_token = request.args.get('access_token') -# html_body = 'Click To Launch Lab' -# response = make_response(html_body) -# return response - -# this function is called by workspace.html javascript -@app.route('/flask/init', methods=['GET']) -def init_workspace(): - workspaces = coder_functions.list_workspaces('a') - workspace = coder_functions.search_workspaces(workspaces, unused_hours) - - workspace_name = workspace['name'] - workspace_status = workspace['latest_build']['status'] - code_url = 'https://code--main--' + workspace_name + \ - '--a.b.akamai-lab.com?folder=/home/coder/workspaces' - origin_url = 'https://origin--main--' + workspace_name + '--a.b.akamai-lab.com' - app.logger.debug('code_url= '+code_url) - app.logger.debug('origin_url= '+origin_url) - - # 1st validation. - # if the selected_workspace is not running, we start it. - if workspace_status != 'running': - if coder_functions.start_workspace(workspace_name): - app.logger.debug('started workspace %s', workspace_name) - else: - abort(500, description="cannot start workspace " + workspace_name) - - # 2nd validation. - # if the selected_workspace agent is not connected, we wait for it to become connected status - # if we started the selected_workspace above, this takes around 30-40 seconds - if coder_functions.is_agent_connected(workspace_name): - # if validations are successful, we create a session token for end-user to connect to code server and origin server. - # session_token = create_token(workspace_name) - # if session_token != '': - # 3rd validation. - # two apps in the workspace should be ready - if coder_functions.is_app_ready(code_url) and coder_functions.is_app_ready(origin_url): - app.logger.info(workspace_name + ': code and origin are ready') - response = make_response('') - # response.set_cookie("coder_session_token", session_token, domain='b.akamai-lab.com') - return response - #else: - # abort(500, description="cannot create session_token for " + workspace_name) - else: - abort(500, description="cannot connect to the workspace " + workspace_name) - -############################################################################################ - -# delete workspaces by status. we can use this to delete all 'failed' workspaces, for example. -# pending, starting, running, stopping, failed, canceling, canceled, deleting, deleted -@app.route('/flask/delete_workspaces_by_status', methods=['GET']) -def delete_workspaces_by_status(): - target_status = request.args.get('status') - workspaces = coder_functions.find_by_status(target_status) - if coder_functions.delete_workspaces(workspaces): - return 'deleted all '+target_status+' workspaces' - else: - return 'failed to delete all '+target_status+' workspaces' - -# update old workspaces -@app.route('/flask/update_old_workspaces', methods=['GET']) -def update_all(): - workspace_age = request.args.get('age') - names = coder_functions.update_old_workspaces(workspace_age) - return names - -@app.route('/flask/update_workspace', methods=['GET']) -def test(): - workspaces = coder_functions.list_workspaces('a') - workspace = coder_functions.search_workspaces(workspaces, unused_hours) - - app.logger.debug('[Before] workspace_name= %s', workspace['name']) - workspace = coder_functions.update_workspace(workspace) - app.logger.debug('[After] workspace_name= %s', workspace['name']) - - workspace_name = workspace['name'] - workspace_status = workspace['latest_build']['status'] - app.logger.debug('workspace_status= %s', workspace_status) - code_url = 'https://code--main--' + workspace_name + \ - '--a.b.akamai-lab.com?folder=/home/coder' - origin_url = 'https://origin--main--' + workspace_name + '--a.b.akamai-lab.com' - app.logger.debug('code_url= '+code_url) - app.logger.debug('origin_url= '+origin_url) - return workspace_name - - - -@app.route('/flask/upgrade_workspaces', methods=['GET']) -def upgrade_workspaces(): - return 'under construction' - -@app.errorhandler(500) -def internal_error(e): - return jsonify(error=str(e)), 500 diff --git a/python/coder_functions.py b/python/coder_functions.py deleted file mode 100644 index 1ed8a25..0000000 --- a/python/coder_functions.py +++ /dev/null @@ -1,354 +0,0 @@ -from datetime import datetime, timedelta -from flask import current_app -import requests, time, subprocess, re - -# How can we update coder admin_token??? What is the maximum expiration days? -# coder server --max-token-lifetime. Default 2540400 hours = 290 days -# The maximum lifetime duration users can specify when creating an API token. -# coder tokens create --lifetime. Default 720 hours = 30 days -# Specify a duration for the lifetime of the token. -# Create token API key - curl -X POST http://coder-server:8080/api/v2/users/{user}/keys/tokens -# { -# "lifetime": 0, -# "scope": "all", -# "token_name": "string" -# } - -admin_token = 'gNmzL1TLeN-gXfs7q10uWPINqtlpt02Pj' -template_id = 'a5577a86-e700-41be-997b-6001d78061d4' -user_email = 'learn@akamai.com' -user_pwd = 'Qodnwk=$s8' - -token_header = {'Coder-Session-Token': admin_token, - 'Accept': 'application/json'} -token_param = {'email': user_email, 'password': user_pwd} - -# unused hours of workspace -unused_hours = 2 - -# workspace ttl ms -ttl = 86400000 # 24 hours - -######################################################################################### -# functions for end-users start -# functions below are invoked when end-user click the link/button to get a workspace -# list_workspaces(), search_workspaces(), create_workspace_name, update_workspace(), is_workspace_running(), is_agent_connected(), is_app_ready() -########################################################################################## - -# this function get the all workspaces under the specified coder owner -def list_workspaces(owner): - current_app.logger.debug('workspace_owner= %s', owner) - url = 'http://localhost:3000/api/v2/workspaces?owner=' + owner - response = requests.get(url, headers=token_header) - s_code = response.status_code - body = response.json() - current_app.logger.debug('list_workspace status code: '+str(s_code)) - workspaces = [] - if s_code == 200: - for workspace in body['workspaces']: - if workspace['owner_name'] == owner: - workspaces.append(workspace) - else: - current_app.logger.info('cannot get workspaces list') - workspaces = None - - return workspaces - -# this function is main logic which finds an available workspace and return it -def search_workspaces(workspaces, hours): - pattern = r'^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{1-6}Z$' - selectedWorkspace = None - email = None - current_app.logger.debug('hours= '+str(hours)) - # current_app.logger.debug('workspaces= '+str(workspaces)) - for workspace in workspaces: - current_app.logger.debug('workspace= '+str(workspace)) - is_selected = False - workspace_name = workspace['name'] - current_app.logger.debug('workspace_name= '+workspace_name) - - # 'last_used_at' looked useful. But it is updated too late. 5-10 minutes gap. - # so, we gave up using it. - # two code lines below remains just to show the debugging information. - last_used_at = workspace['last_used_at'] - current_app.logger.debug('last_used_at= %s', last_used_at) - - # Each time a workspace is assigned to an end-user, we rename the workspace to the current time. - # so, we use workspace name as 'last_assigned_at' variable - last_assigned_at = datetime.strptime(workspace['name'], '%Y%m%d%H%M%S') - current_app.logger.debug('last_assigned_at= %s', last_assigned_at) - currentDate = datetime.now() - current_app.logger.debug('now= %s', currentDate) - # we calculate how many hours has past since the last time the workspace was assigned. - current_app.logger.debug(currentDate - last_assigned_at) - difference = (currentDate - last_assigned_at).total_seconds() / 3600 - current_app.logger.debug('hoursSinceLastAssigned= '+str(difference)+' hour(s)') - - # if the workspace has been unassigned status longer than unused_hours, we select the workspace. - if difference > hours: - is_selected = True - - # if workspace is selected, we update its name to the current timestamp - if is_selected: - selectedWorkspace =update_workspace(workspace) - current_app.logger.debug('selected workspace= '+str(selectedWorkspace)) - break - - if selectedWorkspace == None: - current_app.logger.info('cannot find an available workspace') - # we need to call create_workspaces() asynchronously - - return selectedWorkspace - -# this function creates a new workspace name based on current time -# How can I make sure this function is thread-safe, to avoid duplicate workspace_names??? -def create_workspace_name(): -# past = timedelta(hours=-unused_hours) -# new_name = datetime.now() + past - new_name = str(datetime.now().strftime('%Y%m%d%H%M%S')) - current_app.logger.debug('new_name= %s'+new_name) - return new_name - -# this function updates a selected workspace with a new name created from create_workspace_name() -def update_workspace(workspace): - selected_workspace = None - workspace_id = workspace['id'] - workspace_name = workspace['name'] - url = 'http://localhost:3000/api/v2/workspaces/' + workspace_id - new_name = create_workspace_name() - current_app.logger.debug('new_name= %s', new_name) - req_body = {"username": "a", "name": new_name} - current_app.logger.debug('update_workspace req_body= %s', req_body) - - response = requests.patch(url, json=req_body, headers=token_header) - s_code = response.status_code - current_app.logger.debug('update_workspace status code: %s', str(s_code)) - # body = response.json() - current_app.logger.debug('update_workspace response body: %s', response.content) - - if s_code == 204: - current_app.logger.debug('workspace %s is updated to %s', workspace_name, new_name) - selected_workspace = requests.get('http://localhost:3000/api/v2/users/a/workspace/'+ new_name, headers=token_header).json() - current_app.logger.info('selected_workspace= %s', selected_workspace) - # following code lines double-check whether the workspace is successfully created or not. - # we commented them out. we might want to use them again for debugging later. - # time.sleep(0.5) - # response = requests.get( - # 'http://localhost:3000/api/v2/users/a/workspace/'+new_name, headers=token_header) - # s_code = response.status_code - # current_app.logger.debug(response.content) - #if s_code != 200: - # current_app.logger.info('cannot check update status of workspace ' + workspace_name) - # workspace = response.json() - else: - current_app.logger.info('cannot update workspace %s', workspace_name) - return selected_workspace - -# first, check workspace status -def is_workspace_running(user_id): - workspace_status = 'unknown' - status_url = 'http://localhost:3000/api/v2/users/a/workspace/'+user_id - status_response = requests.get(status_url, headers=token_header) - current_app.logger.debug(user_id + ": is_workspace_running status code: " + - str(status_response.status_code)) - status_json = status_response.json() - - if status_json['latest_build']['status'] == 'running': - current_app.logger.info(user_id + ': workspace is running') - return True - else: - current_app.logger.info(user_id + ': workspace is NOT running') - return False - -# if workspace is not running, start it -def start_workspace(user_id): - command = 'coder start a/'+user_id - result = subprocess.run([command], shell=True, - capture_output=True, text=True) - current_app.logger.debug(user_id + ': ' + result.stdout) - s_code = result.returncode - if s_code == 0: - return True - else: - return False - -# second, check the agent of the workspace -# if it is not ready, wait -def is_agent_connected(user_id): - agent_status = 'unknown' - count = 0 - while agent_status != 'connected': - status_url = 'http://localhost:3000/api/v2/users/a/workspace/'+user_id - status_response = requests.get(status_url, headers=token_header) - current_app.logger.debug( - user_id + ": is_agent_connected status code: " + str(status_response.status_code)) - status_json = status_response.json() - # app.logger.debug(user_id + ': '+str(status_json)) - if len(status_json['latest_build']['resources']) > 1: - for resource in status_json['latest_build']['resources']: - if resource['type'] == 'docker_container': - for agent in resource['agents']: - if agent['name'] == 'main': - agent_status = agent['status'] - current_app.logger.info( - user_id + ": agent status: " + agent_status) - if agent_status == 'timeout' or agent_status == 'connected': - break - count += 1 - # will check coder is ready for the new url - time.sleep(1) - - current_app.logger.debug(user_id + ": api call count: "+str(count)) - - if agent_status == 'connected': - return True - else: - return False - -# third, check the app running inside the workspace -# there are 2 apps. coder-server and OWASP juice-shop -def is_app_ready(url): - is_ready = False - while True: - response = requests.get(url) - s_code = response.status_code - current_app.logger.debug('%s status code= %s', url, s_code) - if s_code == 200: - is_ready = True - break - else: - time.sleep(1) - return is_ready - -######################################################################################## -# functions for end-users end -######################################################################################## - - - -########################################################## -# The following functions are for administrator only!!! # -######################################################### - -# create a workspace -def create_workspace(): - url = 'http://localhost:3000/api/v2/organizations/1a453979-dbd0-49f5-8d4c-5188c9027466/members/a/workspaces' - workspace_name = create_workspace_name() - param = {"name": workspace_name, - "template_id": "a5577a86-e700-41be-997b-6001d78061d4", "ttl_ms": ttl} - - response = requests.post(url, json=param, headers=token_header) - s_code = response.status_code - current_app.logger.info(workspace_name + - ": create_workspace status: " + str(s_code)) - body = response.json() - current_app.logger.debug(workspace_name + ': create_workspace body: ' + str(body)) - if s_code == 201: - current_app.logger.info(workspace_name +" is created") - else: - current_app.logger.info(workspace_name +" cannot create a new workspace") - body = None - return body - -# create workspaces by the input numbers -def create_workspaces(number_of_workspaces): - workspaces = [] - for i in range(number_of_workspaces): - workspace = create_workspace() - workspaces.append(workspace) - time.sleep(number_of_workspaces) - if len(workspaces) > 0: - current_app.logger.info("workspaces are created") - else: - current_app.logger.info("cannot create any workspace") - workspaces = None - return workspaces - -# find workspaces by status. pending, starting, running, stopping, failed, canceling, canceled, deleting, deleted -def find_by_status(target_status): - workspaces = list_workspaces('a') - new_workspaces = [] - for workspace in workspaces: - status = workspace['latest_build']['status'] - if status == target_status: - new_workspaces.append(workspace) - - return new_workspaces - -# workspace deletion related functions start -def delete_workspace(workspace): - workspace_name = workspace['name'] - owner_name = workspace['owner_name'] - - command = 'coder delete ' + owner_name + '/' + workspace_name + ' -y' - result = subprocess.run([command], shell=True, - capture_output=True, text=True) - current_app.logger.debug(workspace_name + ': ' + result.stdout) - r_code = result.returncode - if r_code == 0: - return True - else: - return False - -def delete_workspaces(workspaces): - new_workspaces = [] - for workspace in workspaces: - workspace_name = workspace['name'] - if delete_workspace(workspace): - current_app.logger.info('deleted workspace %s', workspace_name) - new_workspaces.append(workspace) - else: - current_app.logger.info('failed to delete workspace %s', workspace_name) - if len(new_workspaces) == len(workspaces): - current_app.logger.info('deleted all workspaces') - return True - else: - return False - - -# update workspace' name, if it is older than workspace_age (hours) -def update_old_workspaces(workspace_age): - if isinstance(workspace_age, int) == False: - workspace_age = int(workspace_age) - names = {'old_names': [], 'new_names': []} - workspaces = list_workspaces('a') - regex = r'^\d{14}' - for workspace in workspaces: - need_update = False - old_name = workspace['name'] - - if re.match(regex, old_name): - difference = datetime.now() - datetime.strptime(old_name, '%Y%m%d%H%M%S') - current_app.logger.debug('difference= %s', difference) - if difference.total_seconds()/3600 > workspace_age: - need_update = True - else: - need_update = True - - if need_update: - current_app.logger.debug('workspace= %s', workspace) - current_app.logger.debug('old_name= %s', old_name) - names['old_names'].append(old_name) - workspace = update_workspace(workspace) - current_app.logger.debug('new_name= %s', workspace['name']) - names['new_names'].append(workspace['name']) - # time.sleep(0.5) - return names - -# this function is now used. -def create_token(user_id): - # The token expiry duration for browser sessions. Default 24 hours - # Sessions may last longer if they are actively making requests, - # but this functionality can be disabled via --disable-session-expiry-refresh. - token_url = 'http://localhost:3000/api/v2/users/a/keys' - token_response = requests.post(token_url, headers=token_header) - s_code = token_response.status_code - app.logger.debug('token response status code:' + - str(token_response.status_code)) - if s_code == 201: - token_json = token_response.json() - app.logger.info(user_id + ': a new token is created') - app.logger.debug(user_id + ': '+token_json['key']) - return token_json['key'] - else: - return '' diff --git a/python/docebo_functions.py b/python/docebo_functions.py deleted file mode 100644 index 5b68cbb..0000000 --- a/python/docebo_functions.py +++ /dev/null @@ -1,23 +0,0 @@ -import requests -from flask import current_app - -def docebo_get_user(user_id, docebo_access_token): - if isinstance(user_id, str) == False: - user_id = str(user_id) - current_app.logger.debug('user_id= %s', user_id) - - # View a User's info - # https://akamaisandbox.docebosaas.com/api-browser/#!/manage/User/User_manage_v1_user_user_id - url = 'https://akamaisandbox.docebosaas.com/manage/v1/user/'+user_id - - headers = {'Authorization': 'Bearer '+str(docebo_access_token)} - response = requests.get(url, headers=headers) - s_code = response.status_code - body = response.json() - current_app.logger.debug('docebo_get_user response status code= '+str(s_code)) - if s_code == 200: - docebo_user = body['data']['user_data'] - current_app.logger.debug('docebo_get_user response body= '+str(docebo_user)) - return docebo_user - else: - return None \ No newline at end of file