外部 API の実装例
以下のコード抜粋は、外部 API メソッドを使用した curl での PHP の例を示しています。この例では、ユーザ、デバイス、およびそれら 2 つの間の自動接続ポリシーをプロビジョニングします。API リクエストで にアクセスできるクライアント ソフトウェアを準備するには、
CA Technologies
プロフェッショナル サービスを利用できます。capamnew
以下のコード抜粋は、外部 API メソッドを使用した curl での PHP の例を示しています。この例では、ユーザ、デバイス、およびそれら 2 つの間の自動接続ポリシーをプロビジョニングします。API リクエストで
Privileged Access Manager
にアクセスできるクライアント ソフトウェアを準備するには、CA Technologies
プロフェッショナル サービスを利用できます。<?phpclass APIConstants{ const DEVICE_ENDPOINT_V1 = "/api.php/v1/devices.json"; const DEVICE_GROUP_ENDPOINT_V1 = "/api.php/v1/devicegroups.json"; const GET = "GET"; const POLICIES_ENDPOINT_V1 = "/api.php/v1/policies.json"; const ROLE_GLOBAL_ADMINISTRATOR = 1; const ROLE_STANDARD_USER = 2; const ROLE_OPERATIONAL_ADMINISTRATOR = 14;const POST = "POST"; const PUT = "PUT"; const TWO_DAYS = 172800; const USER_ENDPOINT_V1 = "/api.php/v1/users.json"; const USER_GROUP_ENDPOINT_V1 = "/api.php/v1/userGroups.json";}/** ** This function will make a single request to the API. * @param string $apiKey - api key name and password delimited by colon * @param string $url - the URL to reach the desired endpoint of the API. * For a get may include parameters * @param string $postData - JSON encoded set of parameters * @param string $httpOperation - GET, POST, PUT, or DELETE * @return string -1 for failure, otherwise results of request */function makeAPIRequest($apiKey, $url, $postData = null, $httpOperation) { global $debug; $httpOperation = strtoupper($httpOperation); if(!in_array($httpOperation,array("GET","POST","PUT","DELETE"))){ return -1; }/* In real code the url could be validated. This is left out as a distraction to the point of the cookbook. */if(!empty($postData) && is_null(json_decode($postData))){ error_log("Invalid post data " . print_r($postData,true) . "\n Post data must be in JSON format."); return -1; }// apiKey must have at least one colon, and not in the first position if(strpos($apiKey,":") == 0){ error_log("Incorrectly formated api key. Key must consist of api key name, a colon, and the api password"); return -1; } $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5); curl_setopt($ch, CURLOPT_TIMEOUT, 30); curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_ANY); curl_setopt($ch, CURLOPT_USERPWD, $apiKey); switch($httpOperation){ case "GET": break; case "PUT": curl_setopt($ch, CURLOPT_CUSTOMREQUEST,"PUT"); // absence of break is intentional case "POST": curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $postData); curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json','Content-Length: ' . strlen($postData))); break; case "DELETE": curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "DELETE"); } /* * These are useful debug statements */ if($debug){ echo "XXX: URL = " . $url . PHP_EOL; echo "YYY: parameters = " . print_r($postData, true) . PHP_EOL; echo "ZZZ: httpOperation = " . $httpOperation . PHP_EOL; } $data = curl_exec($ch); if($debug){ echo "AAA: return = " . print_r($data,true) . PHP_EOL; }$error = curl_error($ch); if(!empty($error)){ error_log("CURL request to $url returned error: $error"); $data = -1; }curl_close($ch); return trim($data);}/* assume following parameters * argv[1] = URL component e.g, http://10.1.10.24/ port may be included * argv[2] = user name for REST API * argv[3] = password for REST API * argv[4] = first name of user to be provisioned * argv[5] = last name of user * argv[6] = email address of user * argv[7] = device name * argv[8] = domain name * agrv[9] = operating system * argv[10] = user name for target account * argv{11] = debug 0 for false any positive for true */if(count($argv) != 12){ // in real code more information would be supplied echo " Missing required parameters. ". PHP_EOL; return ;}$baseURL = $argv[1];$apiKey = $argv[2]. ":" . $argv[3];$firstName = $argv[4];$lastName = $argv[5];$email = $argv[6];$device['deviceName'] = $argv[7];$device['domainName'] = $argv[8];$device['os'] = $argv[9];$userAccountName = $argv[10];$debug = $argv[11]; $userName = $firstName . "_" . $lastName; /* * Determine if the user already exists. * The user name has to be unique, but since all searches are 'contains ' style, add the first and last names * to reduce the number of substring hits. For this first example we will code the URL manually */$url = "https://" . $baseURL . APIConstants::USER_ENDPOINT_V1 . "?userName=" .urlencode($userName) ."&firstName=" . urlencode($firstName) ."&lastName=" . urlencode($lastName) ."&fields=userId,userName,expiration,roles";$userData = makeAPIRequest($apiKey, $url, null,APIConstants::GET);//the true in the decode parameter list makes the JSON be turned into PHP associative arrays,// rather than a mix of arrays and stdClass objects.$userList = json_decode($userData,true); /* * if the user is not found, create it. Have it immediately active, but expiring in 48 hours * from now */if($userList['totalRows'] === 0){ // The return from creating a new user is the id of the newly created user $userId = buildNewUser($userName,$firstName,$lastName,$email); // add error checking if($userId == -1){ echo " Failed to add new user " . $userName .". Aborting"; return; }}else{ /* // if the user already exists then // update the expiration time by two days unless the expiration date is set to unlimited or * later than 2 days away // add standard user to the list of roles if they don't already have the role */ unset($user); foreach($userList['users'] AS $userCandidate){ // There can be only one exact match on userName if($userCandidate['userName'] == $userName){ // null is returned for a successful update $result = updateUser($userCandidate); if(!empty($result)){ echo "Update failed with result " . print_r($result,true) .PHP_EOL; } $userId = $userCandidate['userId']; break; } }}/*// Add the user to a group, either to give it a desired set of privileges or * to let the user have access to group level policies */if(isset($userId)){ addUserToGroup($userId, "Standard Role Users");}/* now to process the device. Do an OR search to find any devices that match either the device name * or the domain name */$searchParameters = $device;unset($searchParameters['os']);// add extra fields to make device usable - assume typeAccess$device['typeAccess'] = 't';$deviceList = findDevice($searchParameters,"OR", "deviceId,deviceName,domainName,os,typePassword,typeAccess,deviceAccessMethods");if(isset($deviceList['totalRows'])){// cases 0 matches - go ahead and create it switch($deviceList['totalRows']){ case 0: $deviceId = buildNewDevice($device); $device['deviceId'] = $deviceId; // now add an access method $accessMethodId = updateDevice($device); break; case 1: // confirm both dom name and device name match // check for access method if missing add it. $deviceCandidate = $deviceList['devices'][0]; if($deviceCandidate['deviceName'] == $device['deviceName'] && $deviceCandidate['domainName'] == $device['domainName']){ $accessMethodId = updateDevice($deviceCandidate); $deviceId = $deviceCandidate['deviceId']; $device['deviceId'] = $deviceId; }else{ // conflict echo "Device retrieved was " . $deviceCandidate['deviceName'] . " with a domain name of " . $deviceCandidate['domainName'] . PHP_EOL; echo "Device searched for was " . $device['deviceName'] . " with a domain name of " . $device['domainName'] . PHP_EOL; return -1; } break; default: // find the device that has an exact hit if any and update it foreach($deviceList['devices'] AS $deviceCandidate){ $foundDevice = false; if($deviceCandidate['deviceName'] == $device['deviceName'] && $deviceCandidate['domainName'] == $device['domainName']){ $accessMethodId = updateDevice($deviceCandidate); $deviceId = $deviceCandidate['deviceId']; $device['deviceId'] = $deviceId; $foundDevice = true; break; } } if(!$foundDevice){ echo "Could not find device with name " . $device['deviceName'] . " and domain name of " . $device['domainName'] . PHP_EOL; return -1; } }}else{ /* * problem with query */ echo "Device retrieve query had a problem. Details were " . print_r($deviceList,true) . PHP_EOL; echo "Aborting." . PHP_EOL; return;}/* * create a policy between the user and the device using the access method we added */$policy = findExistingPolicy($userId,$deviceId);if($policy === 0){ $policyId = addPolicy($userId,$deviceId,$accessMethodId); /* * if we found a policy then we returned the details */}elseif(is_array($policy)){ $policyId = $policy['id']; /* * otherwise something went wrong */}elseif ($policy == -1){ return;}// check to see if a target application for this device already exists$targetApplicationId = findTargetApplication($device);/* * check to see if array returned, if so check for error code */if(is_array($targetApplicationId)){ foreach($targetApplicationId AS $errorMessage){ /* * Message 5186 says Device not found or is not a target server. * Since the device must exist because we found it earlier, it must not be a target server. * Update the device to be of typePassword (i.e., a target server). */ if(strpos($errorMessage['message'],"5186")){ $results = updateDeviceTargetServer($device['deviceId'], 't'); /* * A successful update returns nothing. */ if(empty($results)){ $targetApplicationId = 0; break; }else{ /* * More error processing goes here */ } } }}if(empty($targetApplicationId)){ /* * To demonstrate error handling we will try to add a target application despite the fact that * the device is not typePassword */ $targetApplicationId = addTargetApplication($device);}if(!is_numeric($targetApplicationId) || $targetApplicationId < 1){ // error time to abort return;}// if needed add a target account for auto-connect to the target application$targetAccountId = findTargetAccount($deviceId,$targetApplicationId,$userAccountName);if(empty($targetAccountId)){ $targetAccountId = addTargetAccount($deviceId,$targetApplicationId,$userAccountName);}$policy = findExistingPolicy($userId,$deviceId);if($policy === 0){ $policyId = addPolicy($userId,$deviceId,$accessMethodId);}elseif(is_array($policy)){ $policyId = $policy['id'];}elseif ($policy == -1){ return;}// retrieve the policy again and add the target application for auto-connect$policy = findExistingPolicy($userId,$deviceId);addSSOToPolicy($policy,$accessMethodId,$targetAccountId);function buildNewUser($userName,$firstName,$lastName,$email){ global $apiKey, $baseURL; // We can either use stdClass or an associative array to build POST or PUT data. // This example uses stdClass $user = new stdClass(); $user->userName = $userName; $user->firstName = $firstName; $user->lastName = $lastName; $user->email = $email; $user->roles = array(array("roleId"=>2,"userGroups"=>array(),"deviceGroups"=>array())); $user->password = "password"; $user->expiration = time() + APIConstants::TWO_DAYS; $parameters = new stdClass(); $parameters->data = $user; $addUrl = "https://" . $baseURL . APIConstants::USER_ENDPOINT_V1; return makeAPIRequest($apiKey, $addUrl, json_encode($parameters),APIConstants::POST);}/* * Another way to give users certain roles is to assign them to a user group with those roles. * As an example we will get the id for a group called Standard Role Users * This example uses the php http_build_query function to generate the URL encoded parameters */function addUserToGroup($userId,$groupName){ global $apiKey,$baseURL; $url = "https://" . $baseURL . APIConstants::USER_GROUP_ENDPOINT_V1 . "?" .http_build_query(array("groupName"=>$groupName,"fields"=>"groupId,groupName,description")); $groupData = makeAPIRequest($apiKey, $url,null, APIConstants::GET); if($groupData == -1){ echo "Failed to get user group list. User " . $userId . " will not be added to the Standard Role Users group" . PHP_EOL; } //the true in the decode parameter list makes the JSON be turned into PHP associative arrays, // rather than a mix of arrays and stdClass objects. $groupList = json_decode($groupData,true);if(isset($groupList['totalRows'])){ switch($groupList['totalRows']){ case 0: break; case 1: $groupId = $groupList['groups'][0]['groupId']; echo "groupId " . $groupId .PHP_EOL; break; default: foreach($groupList['groups'] AS $userGroup){ if("Standard Role Users" == $userGroup['groupName']){ $groupId = $userGroup['groupId']; break 2; } } } if(isset($groupId)){ $url = "https://" .$baseURL . APIConstants::USER_ENDPOINT_V1 . "/" . $groupId . "/users/" . $userId; $result = makeAPIRequest($apiKey, $url, null, APIConstants::POST); } }else{ echo "totalrows not found" . PHP_EOL; }} function updateUser($userCandidate){ global $apiKey,$baseURL; $user['userId'] = $userCandidate['userId']; $userId = $userCandidate['userId']; if(!empty($userCandidate['expiration'])){ $newExpirationTime = time() + APIConstants::TWO_DAYS; $user['expiration'] = ($newExpirationTime > $userCandidate['expiration']) ? $newExpirationTime : $userCandidate['expiration']; } $addStandardUsers = true; if(count($userCandidate['roles']) > 0){ foreach($userCandidate['roles'] AS $role){ if(in_array($role['roleId'],array(APIConstants::ROLE_STANDARD_USER,APIConstants::ROLE_GLOBAL_ADMINISTRATOR, APIConstants::ROLE_OPERATIONAL_ADMINISTRATOR))){ $addStandardUsers = false; break; } } } if($addStandardUsers){ $user['roles'] = $userCandidate['roles']; $user['roles'][] = array("roleId"=>APIConstants::ROLE_STANDARD_USER, "userGroups"=>array(), "deviceGroups"=>array()); } $updateUrl = "https://" . $baseURL . APIConstants::USER_ENDPOINT_V1; $parameters['data'] = $user; $result = makeAPIRequest($apiKey, $updateUrl, json_encode($parameters),APIConstants::PUT); return $result;}/** * * @param array $searchParms - keys are search fields value are values * @param string $searchRelationShip AND or OR if there are multiple search parameters * @param string $fields what information about a device you want returned. NULL takes the * default the API returns */function findDevice(array $searchParameters,$searchRelationship="AND",$fields=null){ global $apiKey,$baseURL; $searchParameters['searchRelationship'] = $searchRelationship; if(!empty($fields)){ $searchParameters['fields'] = $fields; } $url = "https://" . $baseURL . APIConstants::DEVICE_ENDPOINT_V1 . "?" . http_build_query($searchParameters); $deviceData = makeAPIRequest($apiKey, $url, null, APIConstants::GET); $deviceList = json_decode($deviceData,true); return $deviceList;}function findDeviceById($deviceId,$fields=null){ global $apiKey,$baseURL; if(!empty($fields)){ $searchParameters['fields'] = $fields; } $url = "https://" . $baseURL . APIConstants::DEVICE_ENDPOINT_V1 . "/" . $deviceId;}/** * create a new device * @return deviceId (int) * @param array $device */function buildNewDevice($device){ global $apiKey,$baseURL; $url = "https://" . $baseURL . APIConstants::DEVICE_ENDPOINT_V1; $deviceId = makeAPIRequest($apiKey, $url,json_encode($device), APIConstants::POST); $device['deviceId'] = $deviceId; return $deviceId;}/** * Updates a device to add an access method. * @param array $device * @return access method id */function updateDevice(array $device){ global $apiKey,$baseURL; $addAccessMethod = true; if(!empty($device['deviceAccessMethods'])){ foreach($device['deviceAccessMethods'] as $accessMethod){ if((strtoupper($device['os']) == "LINUX" && $accessMethod['type'] == "SSH" && isset($accessMethod['id'])) ){ return $accessMethod['id']; } } } /* * Always add SSH if one isn't there */$accessMethods = array( "type" => "SSH", "port" => 22 ); $url = "https://" . $baseURL . APIConstants::DEVICE_ENDPOINT_V1 . "/" . $device['deviceId'] . "/accessMethods"; $parameters['accessMethods'] = array($accessMethods); $accessMethodJSON = makeAPIRequest($apiKey, $url, json_encode($parameters), APIConstants::POST); /* * We know there is only one entry in the array at most */ $accessMethod = json_decode($accessMethodJSON,true); $addedAccessMethod = $accessMethod[0]; return $addedAccessMethod['id'];}/** * Add a UNIX type target application (Windows Domain/Proxy not supported, Generic too simple) * @param array $device */function addTargetApplication(array $device){ global $apiKey, $baseURL; $results = addUnixTargetApplication($device); if(is_numeric($results)){ $targetApplicationId = $results; }else{ $errors = json_decode($results,true); if(is_array($errors)){ // More error processing here $targetApplicationId = -1; } } // either the actual target application id or -1 for failure to find or error message if one returned return $targetApplicationId;}/** * * @param array $device * @return mixed empty array if no target application found, * int the targetApplication id if found * array of error messages if found */function findTargetApplication($device){ global $apiKey,$baseURL; // first see if the application already exists. Don't specify fields so as to take the default $url = "https://" . $baseURL . APIConstants::DEVICE_ENDPOINT_V1 . "/" . $device['deviceId'] . "/targetApplications"; $parameter['data']['applicationName'] = $device['deviceName'] . " Unix account"; $results = makeAPIRequest($apiKey, $url, json_encode($parameter), APIConstants::GET); $targetApplications = json_decode($results,true); // if an empty array was returned the search was successful and there were no matching target application. if(is_array($targetApplications) && !empty($targetApplications)){ foreach($targetApplications AS $targetApplication){ if(isset($targetApplication['id']) && $parameter['data']['applicationName'] == $targetApplication['applicationName']){ return $targetApplication['id']; }else if(!isset($targetApplication['id'])){ // error code returned echo " Error when trying to search for a target application. Error was " . print_r($targetApplications,true) . PHP_EOL; // since there may be multiple error messages return everything, not just this error return json_decode($results,true); } } }}/** * Add a new target server of type Unix to the specified device * @param array $device * @return Ambigous <string, number> */function addUnixTargetApplication($device){ global $apiKey,$baseURL; $parameter['data']['applicationName'] = $device['deviceName'] . " Unix account"; $parameter['data']['applicationType'] = "unixII"; $attributes = array("sshSessionTimeout"=>60000,"sshPort"=>22,"unixVariant"=>"LINUX", "sshUseDefaultCiphers"=>"true"); $parameter['data']['attributes'] = $attributes; /* * notice how we use exactly the same URL here as in findTargetApplication. * The only difference is that the type of transaction is POST. * The parameters are different, but that isn't part of the URL. */ $url = "https://" . $baseURL . APIConstants::DEVICE_ENDPOINT_V1 . "/" . $device['deviceId'] . "/targetApplications"; $results = makeAPIRequest($apiKey, $url, json_encode($parameter), APIConstants::POST); return $results;}/** * change a device to either be of typePassword (t) or not (f) * @param integer $deviceId * @param string $trueOrFalse */function updateDeviceTargetServer($deviceId,$trueOrFalse){ global $apiKey, $baseURL; // obviously check if $trueOrFalse is t or f $parameter['data']['typePassword'] = $trueOrFalse; $parameter['data']['deviceId'] = $deviceId; $url = "https://" . $baseURL . APIConstants::DEVICE_ENDPOINT_V1; $results = makeAPIRequest($apiKey, $url, json_encode($parameter), APIConstants::PUT);}/** * Find a target account for a particular target application (and hence for a particular device) * @param integer $deviceId * @param integer $targetApplicationId * @param string $accountName * @return mixed - id if successful, error messages if not */function findTargetAccount($deviceId, $targetApplicationId, $accountName){ global $apiKey, $baseURL; // same thing - check if target account exists already $parameter['data']['accountName'] = $accountName; $url = "https://" . $baseURL . APIConstants::DEVICE_ENDPOINT_V1 . "/" . $deviceId . "/targetApplications/" . $targetApplicationId . "/targetAccounts"; $targetAccountResults = makeAPIRequest($apiKey, $url, json_encode($parameter), APIConstants::GET); $targetAccounts = json_decode($targetAccountResults, true); if(is_array($targetAccounts)){ foreach($targetAccounts as $targetAccount){ if($targetAccount['accountName'] == $accountName){ return $targetAccount['accountId']; } } } return $targetAccounts;} /** * * @param int $deviceId * @param int $targetApplicationId * @param string $accountName * @return Ambigous <string, number> int if successful add otherwise */function addTargetAccount($deviceId, $targetApplicationId, $accountName){ global $apiKey, $baseURL; $parameter['data']['accountName'] = $accountName; // special code to tell PA to generate a unique password based on password composition policy $parameter['data']['password'] = "generate_pass"; $parameter['data']['useAliasNameParameter'] = 't'; $parameter['data']['aliasNames'] = $accountName . ",alias" . $accountName; $url = "https://" . $baseURL . APIConstants::DEVICE_ENDPOINT_V1 . "/" . $deviceId . "/targetApplications/" . $targetApplicationId . "/targetAccounts"; $results = makeAPIRequest($apiKey, $url, json_encode($parameter), APIConstants::POST); if(!is_numeric($results)){ // decode if this is a JSON string $checkResults = json_decode($results, true); if(!empty($checkResults)){ $results = $checkResults; } } return $results;}/** * * @param int $userId * @param int $deviceId * @return policy object if found, 0 if no policy, -1 if invalid parameters */function findExistingPolicy($userId,$deviceId){ global $apiKey, $baseURL; $url = "https://" . $baseURL . APIConstants::POLICIES_ENDPOINT_V1 . "/" . $userId . "/" . $deviceId ."?fields=id,accessMethods"; $results = makeAPIRequest($apiKey, $url, null, APIConstants::GET); $policy = json_decode($results,true); if(isset($policy['id'])){ return $policy; } // most likely results are some kind of error message if(is_array($policy) && count($policy) > 0){ foreach($policy AS $message){ // Message 12033 - userid and device id were both valid, but no policy between them exists if(strpos($message['message'],"12033") !== false){ return 0; } // Message 12034 - user or group id specified in policy does not exist if(strpos($message['message'],"12034") !== false){ echo $message['message'] . PHP_EOL; return -1; } // Message 12035 - device or group id specified in policy does not exist if(strpos($message['message'],"12035") !== false){ echo $message['message'] . PHP_EOL; return -1; } // unexpected error echo $message['message'] . PHP_EOL; return -1; } }} /** * Add a policy between a user and a device for an access method, without specifying auto-connection. * @param integer $userId * @param integer $deviceId * @param integer $accessMethodId * @return policy id on success else void */function addPolicy($userId,$deviceId,$accessMethodId){ global $apiKey, $baseURL;$url = "https://" . $baseURL . APIConstants::POLICIES_ENDPOINT_V1 . "/" . $userId . "/" . $deviceId; $accessMethods = array(array("accessMethodId"=>$accessMethodId)); $parameter['accessMethods'] = $accessMethods; // turn on cli recording $parameter['cliRecording'] = "t"; $results = makeAPIRequest($apiKey, $url, json_encode($parameter), APIConstants::POST); if(is_numeric($results)){ return $results; }}/** * Replace the existing access method for the policy with one that has a target account for auto-connection. * @param array $policy * @param integer $accessMethodId * @param integer $targetAccountId */function addSSOToPolicy($policy,$accessMethodId,$targetAccountId){ global $apiKey, $baseURL; /** * Multiple target accounts could be assigned, so the accountIds are an array */ $putData = array(array("accessMethodId"=>$accessMethodId,"accountIds"=>array($targetAccountId))); $url = "https://" . $baseURL . APIConstants::POLICIES_ENDPOINT_V1 . "/" . $policy['id'] . "/accessMethods"; $results = makeAPIRequest($apiKey, $url,json_encode($putData),APIConstants::PUT);}