How to validate parameters in Flask - api

Is there a way to validate a sting in Flask to check for the following. I am looking for it to not only show up in the Swagger documentation but to also catch it as a string validation error if possible
Min length 10
Max length 1,024
Starts with [a-zA-Z]
Contains [a-zA-Z0-9=!##$%^&*()-]
Ends with [a-zA-z0-9]
Here is how I have my endpoint set up so far
from flask import Flask
from flask_restplus import Api, Resource, fields
flask_app= Flask (__name__)
app = Api(app = flask_app,
version = "1.0",
title = "Test API",
description = "Example endpoint to test validation of POST body parameter using Flaskplus")
model = app.model (
'username model',
{
'username': fields.String (
required = True,
description = "Username of the user",
help = "Username cannot be blank, must be of length 10, can start with an upper or lower case letter, can contain upper/lower case, numbers, and the following special characters [=!##$%^&*()-], and must end with an upper/lower case letter or a number",
example = "john.smith123"
)
}
)
name_space = app.namespace ('', 'Main API')
#name_space.route ("/test")
class test (Resource)
#app.doc(
responses = {
200: 'OK',
400: 'Invalid Argument',
500: 'Mapping Key Error'
}
)
#app.expect (model)
def post (self):
try:
username = request.json.get('username')
return {
'status': 'Username valid',
'status_code': 200
}
except KeyError as e:
name_space.abort (
500,
e.__doc__,
status = "Key error",
statusCode = "500"
)

You can use the flask-parameter-validation project in order to do this. You'll be able to implement it with something similar to the following:
from flask_parameter_validation import ValidateParameters, Json
#name_space.route("/test", methods=["POST"])
#ValidateParameters()
def hello(
username: str = Json(
min_str_length=10,
max_str_length=1024,
pattern=r"REGEX"
)
):
return f"Hello, {username}"
You'll need to replace REGEX with a regex pattern that fulfills your start, middle and end criteria.
Hope that this helps!

Related

Karate | String Split for CLI output [duplicate]

I'm unsure about how I can split the response string from an already created feature to obtain the response header "Location" value.
What I've tried
1)
Feature: Create Tariff
Background:
* def result = call read('../../get-user-token.feature')
* def serviceId = call read('create-service.feature')
Scenario: Create Tariff
Given url 'https://app-dev.topbox.pro/tariff-svc/api/v1/tariffs'
And header Authorization = result.response.token_type + " " + result.response.access_token
And request
"""
{
serviceTypeId: '#(serviceId.responseHeaders['Location'].split('/')[1])',
owner: 1,
type: 0,
pencePerMile: '69.69',
minMileage: '1.00',
minCost: 5,
zoneFrom: '',
zoneTo: '',
fixedCost: 0
}
"""
When method POST
Then status 201
Which resulted in...
IntegrationTests.TestSetup.create-tariff: create-tariff.feature:10 -
net.minidev.json.parser.ParseException: Unexpected token L at position
46.
2)
Feature: Create Tariff
Background:
* def result = call read('../../get-user-token.feature')
* def serviceId = call read('create-service.feature').responseHeaders['Location'].split('/')[1]
Scenario: Create Tariff
Given url 'https://app-dev.topbox.pro/tariff-svc/api/v1/tariffs'
And header Authorization = result.response.token_type + " " + result.response.access_token
And request
"""
{
serviceTypeId: '#(serviceId)',
owner: 1,
type: 0,
pencePerMile: '69.69',
minMileage: '1.00',
minCost: 5,
zoneFrom: '',
zoneTo: '',
fixedCost: 0
}
"""
When method POST
Then status 201
Which resulted in...
failed features: IntegrationTests.TestSetup.create-tariff: -unknown-:5
- javascript evaluation failed: read('create-service.feature').responseHeaders['Location'].split('/')1,
TypeError: Cannot read property "Location" from undefined in at
line number 1
NOTE
The specified feature "create-service.feature" does indeed work when isolated and does produce the response header, as shown below
Use lastIndexOf instead of split:
* def location = responseHeaders['Location'][0]
* def serviceId = location.substring(location.lastIndexOf('/') + 1)
You need to use a Javascript function : https://github.com/intuit/karate#javascript-functions
* def greeter = function(name){ return 'hello ' + name }
* assert greeter('Bob') == 'hello Bob'
EDIT:
* def service = { key : "someinfo/myServiceId"}
* def func = function(service){return service.key.split('/')[1]}
* def serviceId = func(service)
* match serviceId == "myServiceId"
I think the first error is due to single quotes inside your expression, try escaping that
like,
And request
"""
{
serviceTypeId: '#(serviceId.responseHeaders.Location[0].split(\'/\')[1])',
owner: 1,
type: 0,
pencePerMile: '69.69',
minMileage: '1.00',
minCost: 5,
zoneFrom: '',
zoneTo: '',
fixedCost: 0
}
"""
Edit: Just now noted each value in responseHeader has a list type value so access it like Location[0]
And your second Approach should be something like this,
* def serviceId = call read('create-service.feature').responseHeaders.Location[0].split('/')[1]
I just face the same issue (.split is not a function), and in my case, I need to convert the data to string first, before using split function.
Here is the custom code from adrien answer:
* def service = { key : "someinfo/myServiceId"}
* def func = function(service){return service.key.toString().split('/')[1]}

Escape response in karate framework [duplicate]

For some reason a variable with a / character get converted to a \/, how do I prevent this?
I start a echo server that listens on localhost:3000 by running npx http-echo-server
I execute the following:
code:
* def CHALLENGE_USER = '/abc/user'
* def loginJson = { user: '#(CHALLENGE_USER)' , name: 'Some Name'}
* print loginJson
* def TEST_URL = 'http://localhost:3000'
Given url TEST_URL+'/session/loginresponse'
And header Content-Type = 'application/json'
And request loginResponseJson
And method put
Then status 200
It prints { "user": "/abc/user", "name": "Some Name" } like I expect.
The http server logs show "--> {"user":"/schemes/ATT_5_55/CH_1","name":"Some Name"}"
Karate shows the result of the echo {"user":"\/abc\/user","name":"Some Name"}
I have tried:
def CHALLENGE_USER = '/abc/user'
def CHALLENGE_USER = "/abc/user"
def CHALLENGE_USER = '/abc/user'
def CHALLENGE_USER = '//abc//user'
also setting the variable after the fact does not work:
* def loginJson = { name: 'Some Name'}
* loginJson.user = CHALLENGE_USER
Yes this is legal as per the JSON spec: JSON: why are forward slashes escaped?
And the Java libraries we use does that.
Does your server have a problem ? If so - then you have a bug that Karate surfaced.
And if you really want to have full control over the request, please use text but IMO it may be a waste of time: https://stackoverflow.com/a/68344856/143475
A nasty workaround, please forgive me Peter Thomas.
You can convert the json to a string and then remove the \ characters.
I only have one use case for this thank goodness.
* def CHALLENGE_USER = '/abc/user'
* def loginJson = { user: '#(CHALLENGE_USER)' , name: 'Some Name'}
* string json = loginJson
* def loginJsonText = json.replaceAll("\\", "")
* print loginJson
* def TEST_URL = 'http://localhost:3000'
Given url TEST_URL+'/session/loginresponse'
And header Content-Type = 'application/json'
And request loginJsonText
And method put
Then status 200

Karate framework variable usage

I have this steps:
...
Then status 200
And match response.requests[0].request.url == "/endpoint"
And json body = response.requests[0].request.body
And match body == { "something": "something"}
To simplify, I tried to put response.requests[0].request in a variable called request:
...
Then status 200
And def request = response.requests[0].request
And match request.url == "/endpoint"
And json body = request.body
And match body == { "something": "something"}
I'm having the following error:
'request' is not a variable, use the form '* request <expression>' instead
I read the documentation and the use of request seems to be fine:
Given def color = 'red '
And def num = 5
Then assert color + num == 'red 5'
What am I doing wrong?
Thanks in advance.
Just make this change:
* def req = response.requests[0].request
# other steps
* request req
We simply disallow def request (using request as a variable name) because a lot of newbie users get confused. The error message has worked 99.9% of the time for users to understand what the problem is, but I guess you fall in the 0.1% :)

Bittrex API Invalid Signature Response With Python

I am trying to view my open orders through the Bittrex API but all I get is an INVALID_SIGNATURE response.
I'm using Python 3.6. Here is my code:
import time
import hmac
import hashlib
import requests
apikey = '12345'
apisecret = '56789'
nonce = str(time.time())
url = 'https://bittrex.com/api/v1.1/market/getopenorders?&apikey=' + apikey + '&nonce=' + nonce
signature = hmac.new(apisecret.encode(), url.encode(), hashlib.sha512).hexdigest()
hdrs = {'apisign' : signature}
r = requests.get(url, headers = hdrs)
print(r.json())
I'm expecting a response like:
{
"success" : true,
"message" : "",
"result" : [{
"Uuid" : null,
"OrderUuid" : "09aa5bb6-8232-41aa-9b78-a5a1093e0211",
"Exchange" : "BTC-LTC",
"OrderType" : "LIMIT_SELL",
"Quantity" : 5.00000000,
…
}
]
}
But instead I get:
{'success': False, 'message': 'INVALID_SIGNATURE', 'result': None}
I know my keys are correct, and using purposely incorrect keys changes the INVALID_SIGNATURE response to
APIKEY_INVALID. I've tried to pull other information such as "getbalance", "getorderhistory", etc., but they all give the same result.
I've found many variations of the code above, but each one I try ends with the same result. I'm sure I'm just missing something simple but after a week of searching, I still don't know why it isn't working.
Any insight is appreciated.
Thanks.

How does ibm - mobile first get mobile number from security context?

I am following "isRegistered" api from this sample code. I did not understand how we get phone number from security context.
The API that I want to use is:
#Path("/isRegistered")
#GET
#Produces("application/json")
#OAuthSecurity(enabled = true)
#ApiOperation(value = "Check if a phone number is registered",
notes = "Check if a phone number is registered",
httpMethod = "GET",
response = Boolean.class
)
#ApiResponses(value = {
#ApiResponse(code = 200, message = "OK",
response = String.class),
#ApiResponse(code = 401, message = "Not Authorized",
response = String.class),
#ApiResponse(code = 500, message = "Cannot check if phone number is registered",
response = String.class)
})
public Boolean isRegistered() {
//Getting client data from the security context
ClientData clientData = securityContext.getClientRegistrationData();
if (clientData == null) {
throw new InternalServerErrorException("This check allowed only from a mobile device.");
}
String number = clientData.getProtectedAttributes().get(SMSOTPSecurityCheck.PHONE_NUMBER);
return number != null && !number.trim().equals("");
}
How does the security context have the client phone number?
There is a client project as well in this sample. Please refer to the complete sample.
Within the client side logic here, the user is asked to provide the phone number , which is sent to the server in an adapter call:
MainViewController.codeDialog("Phone Number", message: "Please provide your phone number",isCode: true) { (phone, ok) -> Void in
if ok {
let resourseRequest = WLResourceRequest(URL: NSURL(string:"/adapters/smsOtp/phone/register/\(phone)")!, method:"POST")
.....
Now in the adapter code path #Path("/register/{phoneNumber}") notice the following code:
clientData.getProtectedAttributes().put(SMSOTPSecurityCheck.PHONE_NUMBER, phoneNumber);
securityContext.storeClientRegistrationData(clientData);
This is how the phone number made it to the security context.
Run the sample and use a tool such as Wireshark to analyze the data flow between client and server.