What is a good strategy to expose an endpoint as public.
Our Taffy API have authentication in every endpoint but we also want to expose some endpoints without authentication.
My Initial strategy is create another Folder in the resources called /public which can bypass the authentication.
We have 2 ways to authenticate.
1. authenticate using an api key in the request
2. Basic Authentication
Our onTaffyRequest
function onTaffyRequest(verb, cfc, requestArguments, mimeExt){
local.status = "forbidden";
local.invalidReturnData = representationOf( local.status ).withStatus(401);
if(structKeyExists(arguments.requestArguments, "apiKey")){
}
/* CATCH NO BASIC auth*/
//if username is blank return false
if (structAuth.username is ""){
return local.invalidReturnData;
}
//check invalid password
if(structAuth.password is ""){
return local.invalidReturnData;
}
return true;
}
Since Taffy version 2.1.0, onTaffyRequest can accept more arguments:
function onTaffyRequest(verb, cfc, requestArguments, mimeExt, headers, methodMetadata){
...
}
(Version 3.0.0 also appended matchedURI to this list)
The methodMetadata argument was added for this purpose. Add something like allow_public="true" to it and inspect for this.
someResource.cfc:
component
extends="taffy.core.resource"
taffy:uri="/foo"
{
function get() allow_public="true" {
return rep({ echo: arguments });
}
}
Application.cfc:
function onTaffyRequest(verb, cfc, requestArguments, mimeExt, headers, methodMetadata, matchedURI){
if ( methodMetadata.keyExists("allow_public") && methodMetadata.allow_public == true ){
return true;
}
// your existing auth-enforcement code should go here
}
Related
I have an account with IBM Bluemix. An external data provider is going to give me the access to one of their web services, which when called will get me data asynchronously. How do I call the web service and capture the data?
You said that the web service is asynchronous, but in the context of a web application, it's more likely that the service is synchronous, but that it is being accessed asynchronously. Most likely, the service is being called for example via AJAX. The call is made to the service, and the execution then continues. When the response comes in, either the success or the failure functions (callbacks) are executed, asynchronously.
If the service itself is asynchronous then you have to develop a more complex client based on some queue logic (or message-driven bean or other).
Both the above described cases need you to develop an application that calls the web service and works on its response.
You can do it using Runtimes or Boilerplates.
There is also the API Connect service, that allows (among the other features) to import an API definition and to test it from the API Designer test tool. However if you need to consume the data and perform some logic on it you'll need an application as described above.
You should be able to call the url's with any language using an AJAX call as mentioned above. If the return type is JSON, then any language should also be able to ingest the results from the AJAX call.
Here's a node.js method I use to get data from my Bluemix applications:
/*
* url: http or https web address that accepts POST..or GET
* callbackFunc: pass the name of a callback function that is in the same scope of makeAjaxCall(). you can pass NULL
* obj: the POST parameters. I use a JSON object
* callbackParams: any parameters you want to pass to your callback function. can be NULL.
*/
function makeAjaxCall(url, callbackFunc, obj, callbackParams) {
var _data = (!isNull(obj)) ? obj : null;
if (!isNull(_data)) {
$.ajax({
url: url,
//dataType: "json",
type: "POST",
data: _data,
success: function (res) {
callbackFunc(jsonStr);
},
error: function (err) {
if (err.status == 500) {
showNotification(err.responseText);
}
showBusy(false);
}
});
}
}
function isNull(inVar) {
if (typeof (inVar) == 'undefined') {
return true;
}
else if (typeof (inVar) == 'string') {
if (inVar == '') {
return true;
}
}
else if (typeof (invar) == 'int') {
if (inVar < 1) {
return true;
}
}
else if (inVar == null) {
return true;
}
return false;
}
For our AWS API Endpoints we use AWS_IAM authorization and want to make a call from Swagger UI.
To make a successful call there must be 2 headers 'Authorization' and 'x-amz-date'. To form 'Authorization' we use following steps from aws doc.
We must to change 'x-amz-date' with every call to go through authorization.
The question is: How to write script in Swagger to sign request, which run every time before request send to aws?
(We know how to specify both headers one time before loading Swagger page, but this process should be re-run before every call).
Thanks in advance.
There is built-in support in swagger-js to add requestInterceptors to do just this. The swagger-ui project uses swagger-js under the hood.
Simply create a request interceptor like such:
requestInterceptor: {
apply: function (request) {
// modify the request object here
return request;
}
}
and apply it to your swagger instance on creation:
window.swaggerUi = new SwaggerUi({
url: url,
dom_id: "swagger-ui-container",
requestInterceptor: requestInterceptor,
Here you can set headers in the request object (note, this is not the standard javascript http request object, inspect it for details). But you do have access to all headers here, so you can calculate and inject them as needed.
You can pretty easily monkeypatch signing from the AWS SDK into SwaggerJS(and thus SwaggerUI). See here
I have a slightly modified SwaggerUI here. Given some AWS credentials and an API ID, it will pull down the Swagger definition, display it in SwaggerUI, and then you can call the API using sigv4.
The Authorizer implementation looks like this:
var AWSSigv4RequestSigner = function(credentialProvider, aws) {
this.name = "sigv4";
this.aws = aws;
this.credentialProvider = credentialProvider;
};
AWSSigv4RequestSigner.prototype.apply = function(options, authorizations) {
var serviceName = "execute-api";
//If we are loading the definition itself, then we need to sign for apigateway.
if (options && options.url.indexOf("apigateway") >= 0) {
serviceName = "apigateway";
}
if(serviceName == "apigateway" || (options.operation && options.operation.authorizations && options.operation.authorizations[0].sigv4))
{
/**
* All of the below is an adapter to get this thing into the right form for the AWS JS SDK Signer
*/
var parts = options.url.split('?');
var host = parts[0].substr(8, parts[0].indexOf("/", 8) - 8);
var path = parts[0].substr(parts[0].indexOf("/", 8));
var querystring = parts[1];
var now = new Date();
if (!options.headers)
{
options.headers = [];
}
options.headers.host = host;
if(serviceName == "apigateway")
{
//For the swagger endpoint, apigateway is strict about content-type
options.headers.accept = "application/json";
}
options.pathname = function () {
return path;
};
options.methodIndex = options.method;
options.search = function () {
return querystring ? querystring : "";
};
options.region = this.aws.config.region || 'us-east-1';
//AWS uses CAPS for method names, but swagger does not.
options.method = options.methodIndex.toUpperCase();
var signer = new this.aws.Signers.V4(options, serviceName);
//Actually add the Authorization header here
signer.addAuthorization(this.credentialProvider, now);
//SwaggerJS/yourbrowser complains if these are still around
delete options.search;
delete options.pathname;
delete options.headers.host;
return true;
}
return false;
};
Currently i am using wso2 api manager 1.9 store , we have a login page before accessing anything in store . it is working fine for now .
Now , there's a requirement that we have a web application on some other domain having the webservice as well to authorize the users , in wso2 api manager store when we login using admin/admin ,, instead of calling its login.jag(for the authorization in wso2 store) , it must be calling that webservice for the authorization and we can use the same credentials as we already using in that web application.
So for this , in login.js (which is called after clicking the login button in store) , i have changed some code like :
ACTUAL CODE
loginbox.login = function (username, password, url,tenant) {
jagg.post("/site/blocks/user/login/ajax/login.jag", { action:"login", username:username, password:password,tenant:tenant },
function (result) {
if (result.error == false) {
if (redirectToHTTPS && redirectToHTTPS != "" && redirectToHTTPS != "{}" &&redirectToHTTPS != "null") {
window.location.href = redirectToHTTPS;
} else if(url){
window.location.href = url;
}else{
window.location.href='site/pages/list-apis.jag';
}
} else {
$('#loginErrorMsg').show();
$('#password').val('');
$('#loginErrorMsg div.theMsg').text(result.message).prepend('<strong>'+i18n.t("errorMsgs.login")+'</strong><br />');
}
}, "json");
CHANGED CODE
loginbox.login = function (username, password, url,tenant) {
$.post(authentication_url,function(result){
if(result.statusCode==200){
//will forward it to list-apis to display the apis
window.location.href='site/pages/list-apis?username=test&password=test&tenant=tenant'
}
});
With this changed code , i am getting the expected response from the webservice which i am calling ,, but not able to keep them in session cookies ,,because before it was calling site/blocks/user/login/ajax/login.jag which will authorize the user and then check for csrf tokens and lot of other things .
Can anyone please let me know where i am missing OR where i need to change so that users from webservice can be authorised .??
Thanks
You cannot pass username and password to /list-api . It does not handle those parameters and set them to session cookie.
window.location.href='site/pages/list-apis?username=test&password=test&tenant=tenant'
I think you might be able to implement something similar to SAML SSO implementation in the api manager. In SAML case, authentication response from the IDP is sent to api manager as a redirection. That request is handled by /store/jagg/jaggery_acs.jag file. Session is set in that location. You might be able to implement similar kind of thing to handle your redirection and set the session there. (I haven't try this)
I am trying my first web app service using Azure services. I've created it in VS, and it works locally. All it does it return a string that says "hello user" is JSON.
[ServiceContract(Namespace = "")]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class Service1
{
// To use HTTP GET, add [WebGet] attribute. (Default ResponseFormat is WebMessageFormat.Json)
// To create an operation that returns XML,
// add [WebGet(ResponseFormat=WebMessageFormat.Xml)],
// and include the following line in the operation body:
// WebOperationContext.Current.OutgoingResponse.ContentType = "text/xml";
[OperationContract]
[WebGet(UriTemplate = "/DoWork")]
public string DoWork()
{
// Add your operation implementation here
return "hello user";
}
// Add more operations here and mark them with [OperationContract]
}
}
Problem is when I publish it, says successful. I can see it running on portal.
When I goto published site I get the standard THIS WEB APP HAS BEEN SUCCESSFULLY CREATED, but... when I add the /DoWork to the URL I get HTTP error 404.
I know I must be missing something simple...
any ideas?
you're missing the name of the service. In your case would be something like:
http://engineappservicev001.azurewebsites.net/something.svc/dowork
More info in here:
http://www.codeproject.com/Articles/571813/A-Beginners-Tutorial-on-Creating-WCF-REST-Services
Im using grails wslite plugin to consume a soap web-service, im able to call the service method from the body section if the parameters are not specified, im getting results from that service method. but I I try to pass the parameters im getting error as
soap:Client - Unmarshalling Error: unexpected element (uri:"htp://soapauth/", local:"parameters"). Expected elements are <{}count>,<{}status>
My soap service method like this ,I'm using grails cxf plugin to expose it as a service
#WebMethod(operationName="getReqMethod", action = "getReqMethod")
String getReqMethod(
#WebParam( name="count") Integer count, #WebParam(name="status") String status ){
print " in service "+count+" -- "+status
}
and the wslite client code in my controller is as follows.
def index(){
withSoap(serviceURL: 'http://mysite.com/SoapAuth/services/sampleReq') {
def response = send(SOAPAction: "getReqMethod") {
header() {
auth {
username("wsuser")
password("secret")
}
}
body{
getReqMethod("xmlns": 'htp://soapauth/')
{
parameters{
count(2)
status("active")
}
}
}
}
println "res "+response.getReqMethodResponse.text()
}
Shouldn't it be just this instead of parameters closure?
getReqMethod("xmlns": 'http://soapauth/') {
count(2)
status("active")
}