How to generate the IAM access tokens for analytics engine requests using python? - ibm-cloud

The documentation for Analytics Engine provides a link to generate a IAM access tokens using the CLI, but I need to generate the token with an API call. This is the CLI approach:
bx api https://api.ng.bluemix.net
bx login
<enter your credentials>
<If you are part of multiple IBM Cloud accounts, you'll be asked to choose an account for the current session. Also, you'll need to choose an organization and space in IBM Cloud.>
bx iam oauth-tokens
The documentation also states that the Cloud Foundry API is deprecated? How can I generate the IAM access tokens?

Here is the code I created in the end ...
Some utility classes for logging and exceptions:
import requests
import json
from datetime import datetime, timedelta
import logging
import os
class Logger:
def __init__(self):
format = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
logging.basicConfig(format=format)
self.ch = logging.StreamHandler()
def get_logger(self, clazz):
logger = logging.getLogger(clazz)
logger.setLevel(os.getenv("LOG_LEVEL", logging.INFO))
return logger
class CloudFoundryException(Exception):
def __init__(self, message, *args):
self.message = message
super(CloudFoundryException, self).__init__(message, *args)
Then a class to do the main work:
class CloudFoundryAPI(object):
def __init__(self, api_key=None, api_key_filename=None, api_endpoint='https://api.ng.bluemix.net', provision_poll_timeout_mins=30):
self.log = Logger().get_logger(self.__class__.__name__)
self.provision_poll_timeout_mins = provision_poll_timeout_mins
assert api_key is not None or api_key_filename is not None, "You must provide a value for api_key or for api_key_filename"
# allow tests to override the api_key_filename parameter
if hasattr(CloudFoundryAPI, 'api_key_filename') and CloudFoundryAPI is not None:
api_key_filename = CloudFoundryAPI.api_key_filename
if api_key_filename is not None:
try:
with open(api_key_filename, 'r') as api_file:
d = json.load(api_file)
try:
self.api_key = d['apikey']
except KeyError:
# The attibute name used to be
self.api_key = d['apiKey']
except:
self.log.error('Error retrieving "apiKey" from file {}'.format(api_key_filename))
raise
else:
self.api_key = api_key
self.api_endpoint = api_endpoint
self.info = self._get_info()
def auth(self):
self.log.debug('Authenticating to CloudFoundry')
url = self.info['authorization_endpoint'] + '/oauth/token'
headers = {
'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8',
'Accept': 'application/x-www-form-urlencoded;charset=utf-8',
'Authorization': 'Basic Y2Y6'
}
data = 'grant_type=password&username=apikey&password={}'.format(self.api_key)
try:
response = requests.post(url, headers=headers, data=data)
response.raise_for_status()
except requests.exceptions.RequestException as e:
self.log.error('Cloud Foundry Auth Response: ' + response.text)
# TODO we should define a custom application exception for this
raise
self.auth_token = response.json()
self.expires_at = datetime.now() + timedelta(seconds=self.auth_token['expires_in']/60)
self.log.debug('Authenticated to CloudFoundry')
def oidc_token(self):
self.log.debug('Retrieving IAM token')
url='https://iam.bluemix.net/identity/token'
data="grant_type=urn:ibm:params:oauth:grant-type:apikey&apikey={}".format(self.api_key)
try:
response = requests.post(url, data=data)
response.raise_for_status()
except requests.exceptions.RequestException as e:
self.log.debug('IAM token response: ' + response.text)
raise
self.oidc_token = response.json()
self.oidc_expires_at = datetime.now() + timedelta(seconds=self.oidc_token['expires_in']/60)
self.log.debug('Retrieved IAM token')
return self.oidc_token
def get_auth_token(self):
if not hasattr(self, 'auth_token') or not hasattr(self, 'expires_at') or datetime.now() > self.expires_at:
self.auth()
return self.auth_token
def get_oidc_token(self):
if not hasattr(self, 'oidc_token') or not hasattr(self, 'oidc_expires_at') or datetime.now() > self.oidc_expires_at:
self.oidc_token()
return self.oidc_token
def _request_headers(self):
auth_token = self.get_auth_token()
access_token = auth_token['access_token']
token_type = auth_token['token_type']
headers = {
'accept': 'application/json',
'authorization': '{} {}'.format(token_type, access_token),
'cache-control': 'no-cache',
'content-type': 'application/json'
}
return headers
def _request(self, url, http_method='get', data=None, description='', create_auth_headers=True):
if create_auth_headers:
headers = self._request_headers()
else:
headers = {}
try:
if http_method == 'get':
response = requests.get(url, headers=headers)
elif http_method == 'post':
response = requests.post(url, headers=headers, data=json.dumps(data))
elif http_method == 'delete':
response = requests.delete(url, headers=headers)
response.raise_for_status()
except requests.exceptions.RequestException as e:
self.log.error('{} : {} {} : {} {}'.format(description, http_method, url, response.status_code, response.text))
raise CloudFoundryException(message=response.text)
try:
self.log.debug('{} : {} {} : {} {}'.format(description, http_method, url, response.status_code, json.dumps(response.json())))
except ValueError:
self.log.debug('{} : {} {} : {} {}'.format(description, http_method, url, response.status_code, response.text))
return response
def _get_info(self):
url = '{}/v2/info'.format(self.api_endpoint)
response = self._request(url=url, http_method='get', description='_get_info', create_auth_headers=False)
return response.json()
You can then use it like so:
cf = CloudFoundryAPI(api_key="xxxx") # or pass api_key_filename
cf.get_auth_token() # get UAA token
cf.get_oidc_token() # get IAM token

Related

How to Yahoo ads conversion tracker name through yahoo api

I want to change Yahoo ads conversion tracker name through API automatically.
But, when I run the following code, that returned an error.
access_token = XXXXXXXXXXXXX
account_id = XXXXXX
conversion_id = XXXXXXX
new_name = "new_name"
header = {"Content-Type": "application/json",\
"Accept": "application/json",\
"Authorization": "Bearer " + access_token}
url = "https://ads-search.yahooapis.jp/api/v8/ConversionTrackerService/set"
data = {'accountId':account_id, 'operand':[{'accountId': account_id, 'conversionTrackerId': conversion_id, "conversionTrackerName": new_name}]}
data = json.dumps(data).encode()
req = urllib.request.Request(url, data=data, method='POST', headers=header)
try:
with urllib.request.urlopen(req) as response:
body = json.loads(response.read())
headers = response.getheaders()
status = response.getcode()
print(headers)
print(status)
print(body)
except urllib.error.URLError as e:
print(e.reason)`
Retuened error is:
{'errors': [{'code': 'L0001', 'message': 'Lower list size.', 'details': [{'requestKey': 'operand', 'requestValue': None}]}], 'rid': '6fab0e1ac60dd2a871831484791976bf', 'rval': None}
I guess the length of "operand" field is 1 and it is right length according to yahoo api document. What I shuold do to fix this error?
I tryied to make length of "operand" field 2. But the result was same.

How do I make a HTTP PUT call from XLRelease to update data in Adobe Workfront?

I am attempting to make an HTTP PUT request from XLRelease to update data in Adobe Workfront. I have been able to successfully login using the API client and GET data. I have also been able to successfully update data using Postman as well as using a native Python script. I am using the HttpRequest library within XLR. I am receiving the same response back in XLR as I am when successfully updating when using Postman, however, the data is not updated when using XLR.
My code is as follows:
import json
WORKFRONT_API_HOST = releaseVariables['url']
WORKFRONT_API_VERSION = releaseVariables['wfApiVersion']
WORKFRONT_API_KEY = releaseVariables['apiKey']
WORKFRONT_USERNAME = releaseVariables['wfUsername']
FI_ID = releaseVariables['target_customer_id']
newPortfolioId = releaseVariables['target_portfolio_id']
WORKFRONT_API_URL = WORKFRONT_API_HOST + WORKFRONT_API_VERSION
def wfLogin():
sessionID = ""
login_endpoint = "/login"
login_request = HttpRequest({'url': WORKFRONT_API_URL})
login_response = login_request.get(login_endpoint + "?apiKey=" + str(WORKFRONT_API_KEY).replace("u'","'") + "&username=" + WORKFRONT_USERNAME, contentType='application/json')
if login_response.getStatus() != 200:
print('# Error logging into WF\n')
print(login_response.getStatus())
print(login_response.errorDump())
sys.exit(1)
else:
json_response = json.loads(login_response.getResponse())
print ("Logged in to WF")
sessionID = json_response['data']['sessionID']
return sessionID
def wfLogout(sessionID):
logout_endpoint = "/logout"
logout_request = HttpRequest({'url': WORKFRONT_API_URL})
logout_response = logout_request.get(logout_endpoint + "?sessionID=" + sessionID, contentType='application/json')
if logout_response.getStatus() != 200:
print('# Error logging out of WF\n')
print(logout_response.getStatus())
print(logout_response.errorDump())
sys.exit(1)
else:
json_response = json.loads(logout_response.getResponse())
print ("Logged out of WF")
result = []
session_id = wfLogin()
if session_id != "":
customer_request = HttpRequest({'url': WORKFRONT_API_URL})
endpoint = '/prgm/%s?sessionID=%s&portfolioID=%s&customerID=%s' % (FI_ID, session_id, newPortfolioId, FI_ID)
jsonObj = "{}"
payload = {}
customer_response = customer_request.put(endpoint, jsonObj, contentType='application/json')
if customer_response.getStatus() != 200:
print('# Error connecting to WF\n')
print(customer_response)
print(customer_response.getStatus())
print(customer_response.errorDump())
sys.exit(1)
else:
response_json = json.loads(customer_response.getResponse())
print ("response_json: ", response_json)
#log out of current session
wfLogout(session_id)
else:
print ("No sessionID is available")
sys.exit(1)

Huobi REST API - signing requests - Verification failure

I have problem with Huobi (cryptoexchange) REST API request-signing. Rules are clear (e.g. here). Example for "v1/account/acounts" - GET with no params:
URL params:
AccessKeyId=dbye2sf5t7-d5829459-bf3aee27-67f62&SignatureMethod=HmacSHA256&SignatureVersion=2&Timestamp=2020-11-11T12%3A17%3A57
Pre-signing text:
GET\napi.huobi.pro\n/v1/account/accounts\nAccessKeyId=dbye2sf5t7-d5829459-bf3aee27-67f62&SignatureMethod=HmacSHA256&SignatureVersion=2&Timestamp=2020-11-11T12%3A17%3A57
Hmac SHA256-Signature of Pre-signing text with private key (verified here):
auZl70i2qsUb7+U9yYEEY1bwzLypWIM7qF1pBAJcvfc=
URL to GET: https://api.huobi.pro/v1/account/accounts?AccessKeyId=dbye2sf5t7-d5829459-bf3aee27-67f62&SignatureMethod=HmacSHA256&SignatureVersion=2&Timestamp=2020-11-11T12%3A17%3A57&Signature=auZl70i2qsUb7%2BU9yYEEY1bwzLypWIM7qF1pBAJcvfc%3D
And the response is:
{"status":"error","err-code":"api-signature-not-valid","err-msg":"Signature not valid: Verification failure [校验失败]","data":null}
What I'm making wrong? I have searched thru many code-examples and no error found...
I had this problem with the same endpoint. I'm not sure what code you're using to sign your requests. Here's mine:
class HuobiAuth(requests.auth.AuthBase):
def __init__(self, api_key: str, secret_key: str) -> None:
self.api_key: str = api_key
self.secret_key: str = secret_key
#staticmethod
def create_sign(p_params: Dict, method: str, host_url: str, request_path: str, secret_key: str):
sorted_params = sorted(p_params.items(), key=lambda d: d[0], reverse=False)
# encode_params = urllib.urlencode(sorted_params)
encode_params = urllib.parse.urlencode(sorted_params)
payload = [method, host_url, request_path, encode_params]
payload = '\n'.join(payload)
payload = payload.encode(encoding='UTF8')
secret_key = secret_key.encode(encoding='UTF8')
digest = hmac.new(secret_key, payload, digestmod=hashlib.sha256).digest()
signature = base64.b64encode(digest)
signature = signature.decode()
return signature
def __call__(self, request: requests.PreparedRequest) -> requests.PreparedRequest:
timestamp = datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S')
params_to_sign = {'AccessKeyId': self.api_key,
'SignatureMethod': 'HmacSHA256',
'SignatureVersion': '2',
'Timestamp': timestamp}
host_name = urllib.parse.urlparse(request.url).hostname.lower()
params_to_sign['Signature'] = self.create_sign(params_to_sign, request.method, host_name, request.path_url, self.secret_key)
request.url += '?' + urllib.parse.urlencode(params_to_sign)
return request
My original problem was that instead of using request.method I had a hardcoded "POST", which I must have copied from the original source.
Example usage:
requests.post(url, json=your_data, auth=HuobiAuth(key, secret))

How do I upload an image to Parse Server using Kotlin/Jvm via Rest Service?

I'm creating a Kotlin/Jvm (without Android Sdk) application that interacts with a instance of a Parse Server (Back4App). Unfortunately, parse doesn't provide a Sdk implementation to use with Java/Kotlin without Android.
So I'm using the rest Api. Now I trying to upload a image from my disk into Back4App file server. In the doc there is snippet using curl. But I wasn't able to translate into a Retrofit service:
curl -X POST \
-H "X-Parse-Application-Id: 4MGgDJ0ZiQloXoSTE2I9VM6YUYIz8EwCKF4pK7zr" \
-H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
-H "Content-Type: image/jpeg" \
--data-binary '#myPicture.jpg' \
https://YOUR.PARSE-SERVER.HERE/parse/files/pic.jpg
So I based my implementation in this article and other snippets from GitHub and created a retrofit service for it:
#Multipart
#POST("/parse/files")
fun upload(
#Part file: MultipartBody.Part
): Call<ResponseBody>
And call:
var file = File("assets/escudo.png")
var requestFile = RequestBody.create(MediaType.parse("**/image"), file)
var body = MultipartBody.Part.createFormData("picture", file.name, requestFile)
var r = getService().upload(body).execute()
I created the retrofit instance as below:
fun getService(): ParserService {
val retrofit = Retrofit
.Builder()
.baseUrl("https://parseapi.back4app.com")
.addConverterFactory(GsonConverterFactory.create())
.client(createClient()).build()
return retrofit.create(ParserService::class.java)
}
fun createClient(): OkHttpClient {
return OkHttpClient.Builder().addInterceptor(createHeadInterceptor()).build()
}
fun createHeadInterceptor(): Interceptor {
return HeaderInterceptor()
}
class HeaderInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response =
chain.run {
val credentials = CredentialsUtils.readCredentials()
log.info { credentials }
proceed(
request().newBuilder()
// .addHeader("Content-Type", "application/json")
.addHeader("Content-Type", "image/png")
.addHeader("X-Parse-Application-Id", credentials.back4appAppId)
.addHeader("X-Parse-REST-API-Key", credentials.back4appRestApiKey)
.build()
)
}
}
I was able to use it to posting Json data (by uncommenting the content/type header). But when I tried to upload an image I receive this response:
Response{protocol=h2, code=400, message=, url=https://parseapi.back4app.com/parse/files}
More info:
-- EDIT
I tried a different approuch without Retrofit, it gives a 201 response code and gives me an objectId, but it doesn't upload the file:
val file2 = File("assets/escudo.png")
val serverUrl = "https://parseapi.back4app.com/classes/myfiles"
val url = URL(serverUrl)
val conn = url.openConnection() as HttpURLConnection
conn.requestMethod = "POST"
conn.doOutput = true
val postData = file2.readBytes()
conn.addRequestProperty("Content-length", postData.size.toString())
conn.setRequestProperty("Content-Type", "image/*")
conn.setRequestProperty("X-Parse-Application-Id", credentials.back4appAppId)
conn.setRequestProperty("X-Parse-REST-API-Key", credentials.back4appRestApiKey)
val outputStream = DataOutputStream(conn.outputStream)
outputStream.write(postData)
outputStream.flush()
println(conn.responseCode)
-- EDIT
Trying now using Khttp:
val file = File("assets/foto.jpg")
val file2 = File("assets/escudo.png")
val serverUrl = "https://parseapi.back4app.com/classes/myfiles"
val files = listOf(FileLike("foto.jpg", file), FileLike("escudo.png", file2))
val response = post(serverUrl, headers = getHeaders(), files = files)
println(response)
println(response.text)
}
fun getHeaders(): Map<String, String> {
return mapOf(
"Content-Type" to "image/*",
"X-Parse-Application-Id" to credentials.back4appAppId,
"X-Parse-REST-API-Key" to credentials.back4appRestApiKey
)
}
Getting this error:
<Response [400]>
{"error":"Unexpected token - in JSON at position 0"}
If you're using Back4App, the correct Server URL is:
https://parseapi.back4app.com/files/pic.jpg

Storage of DriveAPI credentials using oauth2client.django_orm.Storage-class

I want to save the client credentials obtained from Drive API. I tried the following code below, it's working well to store credentials but when I'm accessing the data in upload view it's not returning the credential
views.py
from oauth2client.django_orm import Storage
from drv_app.models import CredentialsModel
#authorization of the client by the user
def authorize_application(request):
#setting flow to get permission and code
flow = OAuth2WebServerFlow(CLIENT_ID, CLIENT_SECRET, OAUTH_SCOPE, REDIRECT_URI, ACCESS_TYPE)
authorize_url = flow.step1_get_authorize_url()
code = request.GET.get('code', '')
if code:
#setting flow step2 to exchage code for access token
credential = flow.step2_exchange(code)
#initialising httplib2 instance and building a DriveAPI service
http = httplib2.Http()
http = credential.authorize(http)
drive_service = build('drive', 'v2', http=http)
user, created = User.objects.get_or_create(username=username, email=email)
#saving credentials to database
if created == True:
storage = Storage(CredentialsModel, 'id', user, 'credential')
storage.put(credential)
return HttpResponseRedirect('/upload/')
else:
return HttpResponseRedirect('/upload/')
else:
return HttpResponseRedirect(authorize_url)
def upload_file(request):
username = request.session['username']
user = User.objects.get(username=username)
storage = Storage(CredentialsModel, 'id', user, 'credential')
credential = storage.get()
http = httplib2.Http()
http = credentials.authorize(http)
drive_service = build('drive', 'v2', http=http)
media_body = MediaFileUpload(FILENAME, mimetype='text/plain', resumable=True)
body = {
'title': 'vishnu_test',
'description': 'A test document',
'mimeType': 'text/plain'
}
file = drive_service.files().insert(body=body, media_body=media_body).execute()
pprint.pprint(file)
return HttpResponse('uploaded')
models.py
from oauth2client.django_orm import FlowField
from oauth2client.django_orm import CredentialsField
class CredentialsModel(models.Model):
id = models.ForeignKey(User, primary_key=True)
credential = CredentialsField()
What I'm doing wrong? Please suggest the necessary improvements.
Replace ForeignKey with OneToOneField in CredentialsModel