HTTP request in Fibaro Home Center with virtual device - request

I use the Fibaro Home Center Lite box and have connected several devices to it.
I would like to create a virtual device to send my data to a web server with HTTP request but I don't know how to build the request.
thanks

For VD buttons your code should be like this:
local http = Net.FHttp("192.168.0.32", 80);
local response = http:GET("/core/api/api.php?param=2335");
In Main Loop section (and in scenes) it will have kinda different syntax:
local http = net.HTTPClient();
http : request('https://api.lifx.com/v1/lights/label:Light1/state.json?selector=label:Light1&power=on&brightness=0.7', {
options = {
method = "PUT",
headers = {
['Authorization'] = 'Bearer cce26ealkadj764'
},
data = ""
},
success = function(response) fibaro:debug (response.data) end,
error = function(err) fibaro:debug ("Error:" .. err) end
});

Related

In ELM, a GET request can't have a body, or is it?

Http.request seems to ignore body when the method is GET
init : () -> ( Model, Cmd Msg )
init _ =
( Loading
, Http.request
{ method = "GET"
, headers = []
, url = "http://127.0.0.1"
, body = Http.stringBody "text/plain" "Hello World!"
, expect = Http.expectWhatever Sent
, timeout = Nothing
, tracker = Nothing
}
)
The sent request has no body (when inspected with browser development tool). ๐Ÿค”
init : () -> ( Model, Cmd Msg )
init _ =
( Loading
, Http.request
{ method = "POST" {- CHANGED TO POST -}
, headers = []
, url = "http://127.0.0.1"
, body = Http.stringBody "text/plain" "Hello World!"
, expect = Http.expectWhatever Sent
, timeout = Nothing
, tracker = Nothing
}
)
But when the method is changed to "POST", it works ! The body contains "Hello World!". ๐Ÿคจ
The API I try to communicate with requires an application/json body in a GET request. Help me ๐Ÿ˜ญ !
PS: Here is what the documentations says:
emptyBody : Body
Create an empty body for your Request. This is useful for GET requests
and POST requests where you are not sending any data.
Which is not clear, because it can be interpreted in two different ways:
This is useful for GET requests and { POST requests where you are not sending any data } .
Or:
This is useful for { GET requests and POST requests } where you are not sending any data.
According to the HTTP specification, GET should not have a body. See for example its description on MDN, which says:
Note: Sending body/payload in a GET request may cause some existing
implementations to reject the request โ€” while not prohibited by the
specification, the semantics are undefined. It is better to just avoid
sending payloads in GET requests.
Elm actually does add the body to the request, but the browser doesn't send it.
To see this, compile this Elm program:
module Main exposing (main)
import Platform
import Http
main =
Platform.worker
{ init = \() -> ((), Http.request
{ method = "GET"
, headers = []
, url = "https://catfact.ninja/fact"
, body = Http.stringBody "text/plain" "hi"
, expect = Http.expectWhatever (\_ -> ())
, timeout = Nothing
, tracker = Nothing
})
, update = \() () -> ((), Cmd.none)
, subscriptions = \() -> Sub.none
}
without optimisation flags to get an index.html file. Part of this file is:
var xhr = new XMLHttpRequest();
xhr.addEventListener('error', function() { done($elm$http$Http$NetworkError_); });
xhr.addEventListener('timeout', function() { done($elm$http$Http$Timeout_); });
xhr.addEventListener('load', function() { done(_Http_toResponse(request.expect.b, xhr)); });
$elm$core$Maybe$isJust(request.tracker) && _Http_track(router, xhr, request.tracker.a);
try {
xhr.open(request.method, request.url, true);
} catch (e) {
return done($elm$http$Http$BadUrl_(request.url));
}
_Http_configureRequest(xhr, request);
request.body.a && xhr.setRequestHeader('Content-Type', request.body.a);
xhr.send(request.body.b);
If you open this file in the browser and step through this part of it in the debugger, you can see that Elm actually does put the given body "hi" in the request.body.b value, passed to xhr.send.
But if you then look in the Network tab in the browser console, you can see that the request doesn't contain a body.
So this is the browser stripping out the body with GET requests, not Elm.

Cloud Tasks App Engine target with relative URI throws Exception 400 : HttpRequest.url is required

I am trying to create a task using Google Cloud Tasks using the Python client google-cloud-tasks==2.1.0 but I am getting an exception that HttpRequest.url is required. I am setting relative url which is a URL handling the task in my app.
The queue exists and has been created using:
gcloud task create queue notifications
The code:
client = tasks_v2.CloudTasksClient()
parent = client.queue_path(project, location, queue)
task = {
'app_engine_http_request': {
'http_method': tasks_v2.HttpMethod.POST,
'relative_uri': notification_url,
'body': payload.encode('utf-8')
},
'http_request': {
'headers': {"Content-type": "application/json"}
}
}
response = client.create_task(parent=parent, task=task)
The exact error that I receive is:
google.api_core.exceptions.InvalidArgument: 400 HttpRequest.url is required
I am trying to create task in my App Engine Standard environment.
#Donald was right, but i think there is a typo in the google docs he linked. I set my headers within app_engine_http_request, not http_request.
I don't think you can provide both app_engine_http_request and http_request, you can only do one. So like this:
client = tasks_v2.CloudTasksClient()
parent = client.queue_path(project, location, queue)
task = {
'app_engine_http_request': {
'http_method': tasks_v2.HttpMethod.POST,
'relative_uri': notification_url,
'headers': {
'Content-Type': 'application/json'
},
'body': payload.encode('utf-8')
}
}
response = client.create_task(parent=parent, task=task)
https://googleapis.dev/python/cloudtasks/latest/tasks_v2/types.html#google.cloud.tasks_v2.types.AppEngineHttpRequest.headers
Your task have two targets which is App Engine and HTTP. On HTTP, a URL is required as specified in creating HTTP target tasks.
The URL must start with 'http://' or 'https://'. To fix the problem, update your http_request:
'http_request': {
'headers': {"Content-type": "application/json"},
'url': "https://[SERVICE-URL]" + notification_url
}
Or, remove http_request and specify your header like this just after declaring your task:
task["http_request"]["headers"] = {"Content-type": "application/json"}
EDIT: When specifying App Engine headers, it is also possible to write it down this way:
task["app_engine_http_request"]["headers"] = {"Content-type": "application/json"}

CORS restriction from AWS when calling a webAPI from a reactJS application

I have a visual basic application that I wrote installed on an AWS elastic beanstalk environment. I also have a reactJS application installed on an AWS S3 instance. Within the reactJS application, I execute the following fetch:
var url = "";
var which = "login";
var passedData = "6007sP0001????DEMI0000000000%20%20%20%20INTERNET/093000050vista19%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20money19%20%20%20";
switch (which) {
case "login":
url = "https://webaccuapi-dv.accurecord-direct.com/api/login?mqRequest=" + passedData;
break;
case "homepage":
url = "https://webaccuapi-dv.accurecord-direct.com/api/homepage?mqRequest=" + passedData;
break;
}
const options = { method: 'GET' };
var returnString = "";
return new Promise((resolve, reject) => {
console.log("do fetch at ", new Date().toLocaleString())
fetch(url, options)
.then(function(response) {
return response.json();
})
.then(function(myJson) {
if (myJson == undefined)
{
returnString = "fetch failed";
}
else
{
returnString = myJson[0];
console.log("return from fetch at ", new Date().toLocaleString())
}
resolve(returnString);
});
});
The code in the visual basic application contains the following code which I found in another stackoverflow article months ago (but I dont remember where):
Imports System.Web.Http
Imports System.Web.Http.Cors
Public Module WebApiConfig
Public Sub Register(ByVal config As HttpConfiguration)
' Web API configuration and services
Dim Cors = New EnableCorsAttribute("http://localhost:8080,http://401kmobileapp.s3-website.us-east-2.amazonaws.com,https://my401kapp.accurecord-direct.com", "*", "*")
config.EnableCors(Cors)
' Web API routes
config.MapHttpAttributeRoutes()
config.Routes.MapHttpRoute(
name:="DefaultApi",
routeTemplate:="api/{controller}/{id}",
defaults:=New With {.id = RouteParameter.Optional}
)
End Sub
End Module
My understanding, as limited as it is, is that the code in the visual basic application should allow requests from http://localhost:8080, http://401kmobileapp.s3-website.us-east-2.amazonaws.com, and https://my401kapp.accurecord-direct.com to process and any request from any other url will be rejected by CORS.
Unfortunately, for some reason, CORS is rejecting my reactJS's requests. When I launch the reactJS app (located at https://my401kapp.accurecord-direct.com/) and I enter a user ID and password (the user ID is 'vista19' and the password is 'money19' - I dont mind sharing these since they are test user ID and passwords) I get the following error in the browser console:
Access to fetch at
'https://webaccuapi-dv.accurecord-direct.com/api/login?mqRequest=6007sP0001????DEMI0000000000%20%20%20%20INTERNET/093000050vista19%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20money19%20%20%20'
from origin 'https://my401kapp.accurecord-direct.com' has been blocked
by CORS policy: No 'Access-Control-Allow-Origin' header is present on
the requested resource. If an opaque response serves your needs, set
the request's mode to 'no-cors' to fetch the resource with CORS
disabled.
I can get the entire process to work from localhost on my development machine if I replace all https with http and as long as I have http://localhost:8080 in the list of urls in my visual basic application. If I remove http://localhost:8080 and run locally, I get a CORS error. If I put http://localhost:8080 back into the list, I get my data back.
So, I know the process works correctly under http but I can not get it to work under https.

How To Setup Minimalist Authentication In Rails with React?

I am trying to set up a minimal layer of authentication between my Rails backend and my React front end, but I am running into some problems.
I cannot seem to find the cookie key value that the server passes down to my client. In the network tab, I see it in the response: Set-Cookie:_skillcoop_session=...., but when I use js-cookie to look for the above cookie, _skillcoop_session, I only see one called identity-token=... and its value is different from _skillcoop_session. How do I access _skillcoop_session in the browser?
What header key do I pass up to the server to signal to my backend to use 'this' header key to match up with the session it has stored off? In this post, Justin Weiss seems to suggest that I make the request to the server with a header like: Cookie: _skillcoop_session=....
Am I doing this all wrong? Would I be better off using a gem like devise?
Also in order to load the session in my other controllers, I have had to do something like session['init'] = true, and I learned to do this from this SO post. This seems hacky. Why do I have to manually reload the session in separate controller actions after I've set it previously in a different controller action in a different request?
I'm currently just stubbing out the user and the authentication -- all I want to do to get the plumping in place is set a session[:user_id] and be able to read that session data in other controller actions. For this I have two main files for consideration: UsersController and Transport.js. In UsersController I am just stubbing the session[:user_id] with the number 1 and in Transport.js I'd like to pass the cookie received from the server so that the backend can maintain a session between requests with a client.
Here is my controller:
class UsersController < ApplicationController
def create
session[:user_id] = 1
render json: user_stub, status: :ok
end
def show
puts "user id: #{session[:user_id]}"
# should return, 1, but is returning, nil...why?
render json: user_stub, status: :ok
end
private
def user_stub
{
id: 1,
email: params['email'] || 'fakeemail#gmail.com',
password: params['password'] || 'fake password'
}
end
end
Here is the main location of my app where I make my request to the server - it's in an abstraction I call Transport.js:
require('es6-promise').polyfill();
require('isomorphic-fetch');
var cookie = require('js-cookie');
const GET = 'GET';
const POST = 'POST';
function Transport() {
}
Transport.prototype.get = function(url, options = {}) {
return this.query(GET, url, null, options);
};
Transport.prototype.post = function(url, dataString, options = {}) {
return this.query(POST, url, dataString, options);
};
Transport.prototype.query = function(method, url, dataString, options = {}) {
var data;
if (dataString) {
data = JSON.parse(dataString);
}
switch(method) {
case GET:
return fetch(url, Object.assign({headers: {'Cookie': cookie.get('_skillcoop_session')}}, options, {
method: method
}));
case POST:
return fetch(url, Object.assign({
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
}, options, {
method: method
}));
default:
throw new Error("This HTTP Method is not supported.");
}
};
module.exports = Transport;
According to this SO post, one cannot access the Set-Cookie header in JS. Thus, I suppose my attempts to handle Set-Cookie in the response headers was a fools effort.
According to the NPM package that I'm using to make HTTP requests, I need to pass {credentials: 'same-origin'} key value pair in the second argument to fetch, which will 'automatically send cookies for the current domain'. That did the trick -- the session object is available and contains the user_id that was set in the session in the previous request in a different action.
Yes. I changed up how I approached this problem. I leaned very heavily on this Reddit post. In short, I use ruby-jwt on the backend and store the token in localStorage on the front end. Each request out to the server will include the token in a header AUTHORIZATION.
In following steps 1 and 2, it looks like I no longer have to 'reload the session'.

Redirect to Identity Server Login page from AngularJs http web api request

I am trying to redirect to Identity Server's default login page when calling an API controller method from Angular's $http service.
My web project and Identity Server are in different projects and have different Startup.cs files.
The web project Statup.cs is as follows
public class Startup
{
public void Configuration(IAppBuilder app)
{
AntiForgeryConfig.UniqueClaimTypeIdentifier = Thinktecture.IdentityServer.Core.Constants.ClaimTypes.Subject;
JwtSecurityTokenHandler.InboundClaimTypeMap = new Dictionary<string, string>();
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "Cookies",
});
var openIdConfig = new OpenIdConnectAuthenticationOptions
{
Authority = "https://localhost:44301/identity",
ClientId = "baseballStats",
Scope = "openid profile roles baseballStatsApi",
RedirectUri = "https://localhost:44300/",
ResponseType = "id_token token",
SignInAsAuthenticationType = "Cookies",
UseTokenLifetime = false,
Notifications = new OpenIdConnectAuthenticationNotifications
{
SecurityTokenValidated = async n =>
{
var userInfoClient = new UserInfoClient(
new Uri(n.Options.Authority + "/connect/userinfo"),
n.ProtocolMessage.AccessToken);
var userInfo = await userInfoClient.GetAsync();
// create new identity and set name and role claim type
var nid = new ClaimsIdentity(
n.AuthenticationTicket.Identity.AuthenticationType,
Thinktecture.IdentityServer.Core.Constants.ClaimTypes.GivenName,
Thinktecture.IdentityServer.Core.Constants.ClaimTypes.Role);
userInfo.Claims.ToList().ForEach(c => nid.AddClaim(new Claim(c.Item1, c.Item2)));
// keep the id_token for logout
nid.AddClaim(new Claim("id_token", n.ProtocolMessage.IdToken));
// add access token for sample API
nid.AddClaim(new Claim("access_token", n.ProtocolMessage.AccessToken));
// keep track of access token expiration
nid.AddClaim(new Claim("expires_at", DateTimeOffset.Now.AddSeconds(int.Parse(n.ProtocolMessage.ExpiresIn)).ToString()));
// add some other app specific claim
nid.AddClaim(new Claim("app_specific", "some data"));
n.AuthenticationTicket = new AuthenticationTicket(
nid,
n.AuthenticationTicket.Properties);
n.Request.Headers.SetValues("Authorization ", new string[] { "Bearer ", n.ProtocolMessage.AccessToken });
}
}
};
app.UseOpenIdConnectAuthentication(openIdConfig);
app.UseResourceAuthorization(new AuthorizationManager());
app.Map("/api", inner =>
{
var bearerTokenOptions = new IdentityServerBearerTokenAuthenticationOptions
{
Authority = "https://localhost:44301/identity",
RequiredScopes = new[] { "baseballStatsApi" }
};
inner.UseIdentityServerBearerTokenAuthentication(bearerTokenOptions);
var config = new HttpConfiguration();
config.MapHttpAttributeRoutes();
inner.UseWebApi(config);
});
}
}
You will notice that the API is secured with bearer token authentication, whereas the rest of the app uses OpenIdConnect.
The Identity Server Startup.cs class is
public class Startup
{
public void Configuration(IAppBuilder app)
{
var policy = new System.Web.Cors.CorsPolicy
{
AllowAnyOrigin = true,
AllowAnyHeader = true,
AllowAnyMethod = true,
SupportsCredentials = true
};
policy.ExposedHeaders.Add("Location");
app.UseCors(new CorsOptions
{
PolicyProvider = new CorsPolicyProvider
{
PolicyResolver = context => Task.FromResult(policy)
}
});
app.Map("/identity", idsrvApp =>
{
idsrvApp.UseIdentityServer(new IdentityServerOptions
{
SiteName = "Embedded IdentityServer",
SigningCertificate = LoadCertificate(),
Factory = InMemoryFactory.Create(
users: Users.Get(),
clients: Clients.Get(),
scopes: Scopes.Get())
});
});
}
X509Certificate2 LoadCertificate()
{
return new X509Certificate2(
string.Format(#"{0}\bin\Configuration\idsrv3test.pfx", AppDomain.CurrentDomain.BaseDirectory), "idsrv3test");
}
}
Notice that I have added a CorsPolicy entry in order to allow the Web App to hopefully redirect to the Login page. In addition, the Cors policy exposes the Location request header, since it contains the url that I would like to redirect to.
The Web Api controller method is secured using the Authorize Attribute, like so
[HttpPost]
[EnableCors(origins: "*", headers: "*", methods: "*")]
[Authorize]
public PlayerData GetFilteredPlayers(PlayerInformationParameters parameters)
{
var playerInformation = composer.Compose<PlayerInformation>().UsingParameters(parameters);
var players = playerInformation.Players
.Select(p => new {
p.NameLast,
p.NameFirst,
p.Nickname,
p.BirthCity,
p.BirthState,
p.BirthCountry,
p.BirthDay,
p.BirthMonth,
p.BirthYear,
p.Weight,
p.Height,
p.College,
p.Bats,
p.Throws,
p.Debut,
p.FinalGame
});
var playerData = new PlayerData { Players = players, Count = playerInformation.Count, Headers = GetHeaders(players) };
return playerData;
}
The angular factory makes a call to $http, as shown below
baseballApp.factory('playerService', function ($http, $q) {
return {
getPlayerList: function (queryParameters) {
var deferred = $q.defer();
$http.post('api/pitchingstats/GetFilteredPlayers', {
skip: queryParameters.skip,
take: queryParameters.take,
orderby: queryParameters.orderby,
sortdirection: queryParameters.sortdirection,
filter: queryParameters.filter
}).success(function (data, status) {
deferred.resolve(data);
}).error(function (data, status) {
deferred.reject(status);
});
return deferred.promise;
}
}});
When this call occurs, the response status is 200, and in the data, the html for the login page is returned.
Moreover, I can see on Chrome's Network tab that the response has a Location header with the url of the Login page. However, if I set up an http interceptor, I only see the Accept header has been passed to the javascript.
Here are the http headers displayed in Chrome's network tab:
The response does not have the Access-Control-Allow-Origin header for some reason.
So I have the following questions:
Is there a way I could get access to the Location header of the response in the angular client code to redirect to it?
How might I be able to get the server to send me a 401 instead of 200 in order to know that there was an authentication error?
Is there a better way to do this, and if so, how?
Thanks for your help!
EDIT:
I have added a custom AuthorizeAttribute to determine what http status code is returned from the filter.
The custom filter code
public class BearerTokenAutorizeAttribute : AuthorizeAttribute
{
private const string AjaxHeaderKey = "X-Requested-With";
private const string AjaxHeaderValue = "XMLHttpRequest";
protected override void HandleUnauthorizedRequest(System.Web.Http.Controllers.HttpActionContext actionContext)
{
var headers = actionContext.Request.Headers;
if(IsAjaxRequest(headers))
{
if (actionContext.RequestContext.Principal.Identity.IsAuthenticated)
actionContext.Response.StatusCode = System.Net.HttpStatusCode.Forbidden;
else
actionContext.Response.StatusCode = System.Net.HttpStatusCode.Unauthorized;
}
base.HandleUnauthorizedRequest(actionContext);
var finalStatus = actionContext.Response.StatusCode;
}
private bool IsAjaxRequest(HttpRequestHeaders requestHeaders)
{
return requestHeaders.Contains(AjaxHeaderKey) && requestHeaders.GetValues(AjaxHeaderKey).FirstOrDefault() == AjaxHeaderValue;
}
I have observed two things from this: first, the X-Requested-With header is not included in the request generated by the $http service on the client side. Moreover, the final http status returned by the base method is 401 - Unauthorized. This implies that the status code is changed somewhere up the chain.
Please don't feel like you have to respond to all the questions. Any help would be greatly appreciated!
You have probably configured the server correctly since you are getting
the login page html as a response to the angular $http call -> it is
supposed to work this way:
angularjs $http
Note that if the response is a redirect, XMLHttpRequest will transparently follow it, meaning that the outcome (success or error) will be determined by the final response status code.
You are getting a 200 OK response since that is the final response as the redirect is instantly followed and it's result resolved as the $http service outcome, also the response headers are of the final response
One way to achieve the desired result - browser redirect to login page:
Instead of redirecting the request server side (from the web project to the Identity Server) the web api controller api/pitchingstats/GetFilteredPlayer could return an error response (401) with a json payload that contains a {redirectUrl: 'login page'} field or a header that could be read as response.headers('x-redirect-url')
then navigate to the specified address using window.location.href = url
Similar logic can often be observed configured in an $httpInterceptors that handles unauthorized access responses and redirects them to the login page - the redirect is managed on the client side

Resources