Python Zeep : 401 Untheorized - python-3.x

I am trying to run the below document using zeep in python.
Document
Name: wsdlGetSkillCategories
Binding: FlexForceBinding
Endpoint: https://api.quinyx.com/FlexForceWebServices.php
SoapAction: uri:FlexForce/wsdlGetSkillCategories
Style: rpc
Input:
use: encoded
namespace: uri:FlexForce
encodingStyle: http://schemas.xmlsoap.org/soap/encoding/
message: wsdlGetSkillCategoriesRequest
parts:
apiKey: xsd:string
Output:
use: encoded
namespace: uri:FlexForce
encodingStyle: http://schemas.xmlsoap.org/soap/encoding/
message: wsdlGetSkillCategoriesResponse
parts:
return: tns:SkillCategories
Namespace: uri:FlexForce
Transport: http://schemas.xmlsoap.org/soap/http
Documentation:
My Attempt:
from requests import Session
from requests.auth import HTTPBasicAuth
from zeep.transports import Transport
wsdl = "https://api.quinyx.com/FlexForceWebServices.php?wsdl"
username = "username"
password = "pass"
session = Session()
session.auth = HTTPBasicAuth(username, password)
client = Client(wsdl, transport=Transport(session=session))
data = {"apiKey": "0000-0000-0000-0000"}
result = client.service.wsdlGetSkillCategories(**data);
text1 = (result.text)
print(text1)
Error:
Fault: 401 Unauthorized
Does anybody have any idea why I am getting this error?

Related

How to Send Array String in Zeep

I have this below soap envelope where I have send string array.
<apiKey xsi:type="xsd:string">Ab12-Ab12-Ab12-Ab12</apiKey>ยง
<baseScheduleRollout xsi:type="flex:baseScheduleRollout" xmlns:flex="http://app.quinyx.com/soap/FlexForce">
<badgeNos xsi:type="flex:ArrayOfString" soapenc:arrayType="xsd:string[]">
<item1>1022</item1>
<item2>1030</item2>
<item2>10112330</item2>
</badgeNos>
<fromDateTime xsi:type="xsd:dateTime">2020-11-05T00:00:00</fromDateTime>
<toDateTime xsi:type="xsd:dateTime">2020-11-24T00:00:00</toDateTime>
I know how to send a soap request and my complextype is okay. But I think my string array passing system is wrong. Though I tried to use anyObject but still not working
from zeep import Client
from zeep import xsd
import datetime
from requests import Session
from requests.auth import HTTPBasicAuth
from zeep.transports import Transport
wsdl = "https://api.quinyx.com/FlexForceWebServices.php?wsdl"
session = Session()
session.auth = HTTPBasicAuth('username', 'password')
client = Client(wsdl, transport=Transport(session=session))
ArrayOfString = client.get_element('ns0:ArrayOfObject')
badgeNo = ArrayOfString(['146223'])
data = { "apiKey" : "apikey" ,
"baseScheduleRollout" :{
"badgeNos":badgeNo,
"fromDateTime":xsd.SkipValue,
"toDateTime":xsd.SkipValue
}
}
result = client.service.wsdlGetBaseScheduleRolledoutHours(**data)
result
Error
LookupError: No element 'ArrayOfObject' in namespace https://api.quinyx.com/soap/FlexForce. Available elements are: -

How deserialize protocol buffers from http response use python

I want to deserialize an API response to a python object whose content-type is protobuf, I use the ParseFromString method to parse the HTTP response, but only get a number 23, print the response content directly is b'\n\x08Hi,py-pb'. So, how do I deserialize the HTTP response to the python object?
proto file content:
syntax = "proto3";
package greet;
// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply);
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings.
message HelloReply {
string message = 1;
}
python code:
# _*_ coding: utf8 _*_
from google.protobuf.json_format import ParseDict, MessageToJson
from protos.greet_pb2 import HelloRequest, HelloReply
import httpx
import asyncio
async def send_req():
req = {'name': 'py-pb'}
msg = ParseDict(req, HelloRequest())
print(msg)
print(type(msg))
print(msg.SerializeToString())
async with httpx.AsyncClient() as client:
resp = await client.post('http://localhost:5044/greet/Greeter/SayHello', data=msg.SerializeToString(),
headers={'Accept': 'application/protobuf', 'Content-Type': 'application/protobuf'})
print('=========response=========')
# print(resp.stream)
# print(resp.content)
# print(resp.text)
resp_msg = HelloReply().ParseFromString(resp.content)
# resp_msg = HelloReply().SerializeToString(resp.content)
print(resp_msg)
asyncio.run(send_req())
Versions:
Python - 3.10.5
google.protobuf - 4.21.2
Related answer:
ParseFromString is a method -- it does not return anything, but rather fills in self with the parsed content.
Reference:
Google Protocol Buffers (protobuf) in Python3 - trouble with ParseFromString (encoding?)
ParseFromString is an instance method. So you want, e.g.:
hello_reply = HelloReply()
hello_reply.ParseFromString(resp.content)
The docs include an example using ParseFromString.
Here's a repro:
from google.protobuf.json_format import ParseDict, MessageToJson
from greet_pb2 import HelloRequest, HelloReply
rqst = {'name': 'py-pb'}
msg = ParseDict(rqst, HelloRequest())
tx = msg.SerializeToString()
print(tx)
print(tx.hex())
resp = HelloReply()
resp.ParseFromString(tx)
print(resp)
Yields:
b'\n\x05py-pb'
0a0570792d7062
message: "py-pb"
You can take the binary as hex and plug it into protogen to decode it.
Field #1: 0A String Length = 5, Hex = 05, UTF8 = "py-pb"

Unable to get Test run details from ALM API

Using below python code to get all test runs/test instances under a domain and project however it throws an 404 error, able to get the defects not the test runs
import json
import requests
from requests.auth import HTTPBasicAuth
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
almUserName = "007"
almPassword = "#123"
almDomain = "PRO"
almProject = "Mobile"
almURL = "https://alm.com/qcbin/"
qcDefectURL = almURL+"api/domains/"+almDomain+"/projects/"+almProject+"/defects"
qcTestRunURL=almURL+"api/domains/"+almDomain+"/projects/"+almProject+"/runs/?query={name[Run_8-5_00-9-31]}"
print(qcDefectURL)
print(qcTestRunURL)
session = requests.Session()
session.verify = False
auth = session.post(almURL + "authentication-point/authenticate?login-form-required=y",
auth=HTTPBasicAuth(almUserName, almPassword))
#print("Authentication ", auth, auth.text, session.cookies)
site_session = session.post(almURL + "rest/site-session")
#print("Session ", site_session, site_session.text, session.cookies)
check = session.get(almURL + "rest/is-authenticated")
print("Check ", check, check.text)
# Enforce JSON output
session.headers.update({ 'Accept': 'application/json' })
#projects = session.get(qcDefectURL)
TestRuns=session.get(qcTestRunURL)
print(TestRuns.status_code)
print(TestRuns.json())
Tried using various links mentioned in blogs and stackoverflow answers(here,here,here), but not able to resolve
Output: {'Id': 'qccore.general-error', 'Title': 'Not Found', 'ExceptionProperties': None, 'StackTrace': None}
Your query URL ends with
/runs/?query={name[Run_8-5_00-9-31]}
but should be like
/runs?query={name[Run_8-5_00-9-31]}
Further reading: https://admhelp.microfocus.com/alm/en/12.55/api_refs/REST_TECH_PREVIEW/ALM_REST_API_TP.html#REST_API_Tech_Preview/General/Filtering.html

Sending a gmail message

im trying to send a gmail message with the following code:
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
import base64
def create_message(sender, to, subject, message_text):
"""Create a message for an email.
Args:
sender: Email address of the sender.
to: Email address of the receiver.
subject: The subject of the email message.
message_text: The text of the email message.
Returns:
An object containing a base64url encoded email object.
"""
message = MIMEText(message_text)
message['to'] = to
message['from'] = sender
message['subject'] = subject
#return {'raw': base64.urlsafe_b64encode(message.as_string())}
b64_bytes = base64.urlsafe_b64encode(message.as_bytes())
b64_string = b64_bytes.decode()
#body = {'raw': b64_string}
return {'raw': b64_string}
s = "email"
t = "email"
sub = "test subject"
m = "message test"
create_message(s,t,sub,m)
where s = my email(replaced with "email" in this example and t = my other email (again replaced))
It doesnt kick out any errors but doesnt send the message.
Also this code didnt work:
#return {'raw': base64.urlsafe_b64encode(message.as_string())}
as it produced the following error: TypeError: a bytes-like object is required, not 'str'
"I solved that" with this:
b64_bytes = base64.urlsafe_b64encode(message.as_bytes())
b64_string = b64_bytes.decode()
#body = {'raw': b64_string}
return {'raw': b64_string}
edit(1): I do have credentials.json and passed the google api example
Needed to create client_secret.json and paste my credentials there, ctrc+c, ctrl+v an old example and update it to python 3.x, resolved base 64 issue and it works :)
import httplib2
import os
from apiclient import discovery
import oauth2client
from oauth2client import client
from oauth2client import tools
from oauth2client import file
try:
import argparse
flags = argparse.ArgumentParser(parents=[tools.argparser]).parse_args()
except ImportError:
flags = None
SCOPES = 'https://mail.google.com/'
CLIENT_SECRET_FILE = 'client_secret.json'
APPLICATION_NAME = 'Gmail API Quickstart'
def get_credentials():
"""Gets valid user credentials from storage.
If nothing has been stored, or if the stored credentials are invalid,
the OAuth2 flow is completed to obtain the new credentials.
Returns:
Credentials, the obtained credential.
"""
home_dir = os.path.expanduser('~')
credential_dir = os.path.join(home_dir, '.credentials')
if not os.path.exists(credential_dir):
os.makedirs(credential_dir)
credential_path = os.path.join(credential_dir,
'gmail-quickstart.json')
store = oauth2client.file.Storage(credential_path)
credentials = store.get()
if not credentials or credentials.invalid:
flow = client.flow_from_clientsecrets(CLIENT_SECRET_FILE, SCOPES)
flow.user_agent = APPLICATION_NAME
if flags:
credentials = tools.run_flow(flow, store, flags)
else: # Needed only for compatability with Python 2.6
credentials = tools.run(flow, store)
print('Storing credentials to ' + credential_path)
return credentials
import base64
from email.mime.audio import MIMEAudio
from email.mime.base import MIMEBase
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
import mimetypes
from httplib2 import Http
from apiclient import errors
from apiclient.discovery import build
credentials = get_credentials()
service = build('gmail', 'v1', http=credentials.authorize(Http()))
def SendMessage(service, user_id, message):
"""Send an email message.
Args:
service: Authorized Gmail API service instance.
user_id: User's email address. The special value "me"
can be used to indicate the authenticated user.
message: Message to be sent.
Returns:
Sent Message.
"""
try:
message = (service.users().messages().send(userId=user_id, body=message)
.execute())
print('Message Id: %s' % message['id'])
return message
except errors.HttpError as error:
print('An error occurred: %s' % error)
def CreateMessage(sender, to, subject, message_text):
"""Create a message for an email.
Args:
sender: Email address of the sender.
to: Email address of the receiver.
subject: The subject of the email message.
message_text: The text of the email message.
Returns:
An object containing a base64 encoded email object.
"""
message = MIMEText(message_text)
message['to'] = to
message['from'] = sender
message['subject'] = subject
b64_bytes = base64.urlsafe_b64encode(message.as_bytes())
b64_string = b64_bytes.decode()
#body = {'raw': b64_string}
return {'raw': b64_string}
#return {'raw': base64.b64encode(message.as_string())}
testMessage = CreateMessage("email","email","test_subject","test_message")
testSend = SendMessage(service, 'me', testMessage)
emails replaced with email in line 106 (second from bottom)

Flask JWT Extended cookie name overide Flask Session Cookie Name

I am using the Flask JWT Extended extension for flask and have built a login application successfully using JWT. I have gone through the tutorial on JWT in Cookies on the JWT extended documentation site using CSRF protection and everything.
What I can't seem to figure out is when using the set_access_cookies() and set_refresh_cookies() methods the JWTs are not saved in a httponly cookie named using the JWT Extended default configuration setting.
app.config.setdefault('JWT_ACCESS_COOKIE_NAME', 'access_token_cookie')
app.config.setdefault('JWT_REFRESH_COOKIE_NAME', 'refresh_token_cookie')
Instead when I debug the return back from the auth call the cookies are saved in the base Flask default configuration instead.
'SESSION_COOKIE_NAME': 'session',
Shouldn't the set_access_cookies() and set_refresh_cookies() methods override the base Flask default configurations as long as make sure to register my app in the JWTManager()?
uscc_login_app = Flask(__name__)
jwt = JWTManager(uscc_login_app)
Or is there something else I missed in the base Flask JWT Extended documentation to ensure that its configuration defaults are used when appropriate?
Updated code via request.
The code is pretty spread out but here is my best shot to include what I think will help.
In init.py:
from flask import Flask, url_for
from flask_restful import Api
from flask_jwt_extended import JWTManager
from resources.auth import Authenticate
from resources.refresh import Refresh
from temp_app.temp import TempApp
from uscc_login.uscc_app_login import *
uscc_login_app = Flask(__name__)
uscc_login_app.config.from_object(os.environ.get('FLASK_ENV'))
jwt = JWTManager(uscc_login_app)
api = Api(uscc_login_app, prefix='/v1')
# Add resources via the add_resource method
api.add_resource(Authenticate, '/login')
api.add_resource(Refresh, '/refresh_token')
login_view = Login.as_view(name='uscc_login')
uscc_login_app.add_url_rule('/login', view_func=login_view, methods=['POST', 'GET'])
In my app.py:
from uscc_login import uscc_login_app
if __name__ == '__main__':
uscc_login_app.run(debug=uscc_login_app.config.get('DEBUG'), threaded=uscc_login_app.config.get('THREADED'),
port=uscc_login_app.config.get('PORT'), host=uscc_login_app.config.get('HOST'))
In my config.py since I am using the Flask config.from_objects
import os
import datetime
uscc_login_app_dir = os.path.abspath(os.path.dirname(__file__))
class BaseConfig:
SECRET_KEY = os.environ.get('USCC_SECRET_KEY') or 'you-will-never-guess'
JWT_SECRET_KEY = os.environ.get('USCC_JWT_KEY') or 'super-secret'
JWT_TOKEN_LOCATION = ['cookies']
JWT_COOKIE_CSRF_PROTECT = True
JWT_HEADER_TYPE = 'JWT'
PROPAGATE_EXCEPTIONS = True
THREADED = True
class DevelopmentConfig(BaseConfig):
DEBUG = True
PORT = 5000 if os.environ.get("PORT") is None else int(os.environ.get("PORT"))
HOST = os.environ.get('HOST') or 'localhost'
if os.environ.get('access_token_expiration') is not None:
JWT_ACCESS_TOKEN_EXPIRES = datetime.timedelta(seconds=int(os.environ.get('access_token_expiration')))
if os.environ.get('refresh_token_expiration') is not None:
JWT_REFRESH_TOKEN_EXPIRES = datetime.timedelta(seconds=int(os.environ.get('refresh_token_expiration')))
So then in my Flask MethodView that contains my login authorization POST I have the following:
auth.py
import sys
import os
from flask import jsonify, request
from flask_restful import Resource
from flask_jwt_extended import create_access_token, create_refresh_token, jwt_refresh_token_required, get_jwt_identity, \
set_access_cookies, set_refresh_cookies
from utilities import Common
class Authenticate(Resource):
#staticmethod
def post():
"""
:return:
"""
api_cred_path = os.environ.get('api_cred_path')
if api_cred_path is None:
response = jsonify({"msg": "Environment Variable 'api_cred_path' is not set."})
response.status_code = 500
return response
if not request.is_json:
response = jsonify({'msg': 'Missing JSON in request'})
response.status_code = 400
return response
params = request.get_json()
user_name = params.get('username')
user_password = params.get('password')
if not user_name:
response = jsonify({'msg': 'Missing username parameter'})
response.status_code = 400
return response
if not user_password:
response = jsonify({'msg': 'Missing password parameter'})
response.status_code = 400
return response
if Common.check_path_exists(api_cred_path):
with open(api_cred_path) as afh:
for line in afh:
file_userid, file_password = line.split('=')
if file_userid == user_name and file_password.strip('\n') == user_password:
access_token = create_access_token(identity=user_name)
refresh_token = create_refresh_token(identity=user_name)
response = jsonify({'login': True})
set_access_cookies(response, access_token)
set_refresh_cookies(response, refresh_token)
# # Identity can be any data that is json serializable
# art = {
# 'access_token': create_access_token(identity=user_name),
# 'refresh_token': create_refresh_token(identity=user_name)}
# response = jsonify(art)
response.status_code = 200
return response
else:
response = jsonify({"msg": "api_cred_path invalid."})
response.status_code = 500
return response
response = jsonify({'msg': 'Bad username or password'})
response.status_code = 401
return response
Could you provide some code to duplicate what you are seeing? When I try running the example token in jwt code (https://github.com/vimalloc/flask-jwt-extended/blob/master/examples/jwt_in_cookie.py) I see the expected cookie values when I login:
$ http :5000/token/auth username=test password=test
...
Set-Cookie: access_token_cookie=<jwt>; HttpOnly; Path=/api/
Set-Cookie: refresh_token_cookie=<jwt>; HttpOnly; Path=/token/refresh
...
So I realized my mistake in this. I was trying to get the access_token_cookie variable to be set from my auth.py which serves as my RESTFUL based microservice of which my login app calls to do the authorization. Realized it won't be available after redirecting back to the caller from the login apps POST method since the cookie was related to the login app UI frontend. So I just base the access and refresh tokens back from the auth.py POST method to the login POST method and let it set the cookies so they are available to the end client.
This was more of design problem than a code problem.

Resources