How to get json structure in Rails? - ruby-on-rails

my application should sent structure via json,
that functions works fine:
function send_json()
{
var formData = form2object('myForm');
var json_data = JSON.stringify(formData, null, '\t');
$.post("", json_data);
}
It create json_data and send it via post.
But I don't understand how to catch this data on another side, can you help me ?
I should take this structure and make some actions with it.
when i send data i got json:
{ "type": "test", "ttl": "1", "amount": "1", "rules": { "rule1": "1", "value1": "4444", "stackcount1": "1", "rule2": "2", "value2": "333", "stackcount2": "2" }}
In the contoller i wrote this:
class AdminController < ApplicationController
def index
#json_string = params[:amount]
if !#json_sting.nil?
#json_string = JSON.parse(params[:amount])
end
end
end
in the view(haml) i wrote:
=#json_string
It doesn't work !
But wireshark shows:
POST /admin/ HTTP/1.1
Host: localhost:3000
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:14.0) Gecko/20100101 Firefox/14.0.1
Accept: */*
Accept-Language: ru-ru,ru;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
DNT: 1
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Referer: http://localhost:3000/admin/
Content-Length: 192
Cookie: _GiftSenderTool_session=BAh7BzoQX2NzcmZfdG9rZW4iMTVyNVJCMTBvcGpKSk5OcFN4bW15YVlpRlF1TUNtb1gwZkY0bTRuRHlsNnM9Ig9zZXNzaW9uX2lkIiU4ZjFjNTNjNTY0MTQ1MGExNmFiODcxZGEyYzU5ZTkxMg%3D%3D--f21bf3b96edc3bc551c8de68acc6da17685d58b4
Pragma: no-cache
Cache-Control: no-cache
{
."type": "test",
."ttl": "1",
."amount": "1",
."rules": {
.."rule1": "1",
.."value1": "4444",
.."stackcount1": "1"
.},
."authenticity_token": "n3B6D/Km9zOgOzlosK/OLHyTCDLk3MyCTlTBinfQcnY="
}
It means that i can't get data.

you have to catch your JSON object in some rails controller action. If your JSON string looks like this:
{ "user": { "id": 1, "name": "Tom" } }
then do it like this:
require "json"
...
def some_action
user = JSON.parse(params[:user])
puts "User id is #{user[:id]}"
...
end

Related

springdoc / Swagger UI not passing OAuth2 token to API

I am using springdoc (v1.5.9) to generate Swagger definitions for an API. After authenticating inside the Swagger UI and executing a secured method, the http request received by the Spring Boot app has no Authentication header. I have confirmed via JS debugger that the Swagger UI received and stored a valid authentication token.
Below are the HTTP request, the Swagger api-docs showing the security scheme defined / applied to the method, the springdoc configuration and a controller.
How do I need to change / add to my Spring configuration to get the Authorization passed to the API from the Swagger UI?
HTTP request received
Request received for GET '/foo/1234':
org.apache.catalina.connector.RequestFacade#71c70030
servletPath:/foo/1234
pathInfo:null
headers:
host: localhost:8070
connection: keep-alive
sec-ch-ua: "Chromium";v="92", " Not A;Brand";v="99", "Google Chrome";v="92"
accept: application/json
sec-ch-ua-mobile: ?0
user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36
sec-fetch-site: same-origin
sec-fetch-mode: cors
sec-fetch-dest: empty
referer: http://localhost:8070/swagger-ui/index.html?configUrl=/v3/api-docs/swagger-config
accept-encoding: gzip, deflate, br
accept-language: en-US,en;q=0.9
cookie: JSESSIONID=A0F9846102153C06D77D6ED5506CC227
Security filter chain: [
WebAsyncManagerIntegrationFilter
SecurityContextPersistenceFilter
HeaderWriterFilter
CsrfFilter
LogoutFilter
BearerTokenAuthenticationFilter
RequestCacheAwareFilter
SecurityContextHolderAwareRequestFilter
AnonymousAuthenticationFilter
SessionManagementFilter
ExceptionTranslationFilter
FilterSecurityInterceptor
]
api-docs
{
"openapi": "3.0.1",
"info": {
"title": "My App",
"version": "v1"
},
"servers": [
{
"url": "http://localhost:8070",
"description": "Generated server url"
}
],
"paths": {
"/foo/{id}": {
"get": {
"tags": [
"foo"
],
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
},
"security": [
{
"custom": [
]
}
]
}
},
"securitySchemes": {
"access_token": {
"type": "oauth2",
"in": "header",
"scheme": "custom",
"flows": {
"authorizationCode": {
"authorizationUrl": "https://login.myauthsever.com/v2/oauth/authorize",
"tokenUrl": "https://login.myauthsever.com/v2/oauth/token",
"scopes": {
}
}
}
}
}
}
}
OpenAPI config
#OpenAPIDefinition(info = #Info(title = "My App", version = "v1"))
#SecurityScheme(scheme = "custom", type = SecuritySchemeType.OAUTH2, in = SecuritySchemeIn.HEADER, name = "access_token",
flows = #OAuthFlows(authorizationCode = #OAuthFlow(
authorizationUrl = "https://login.myauthsever.com/v2/oauth/authorize",
tokenUrl = "https://login.myauthsever.com/v2/oauth/token", scopes = {})))
public class OpenApiConfig {
}
Controller
#RestController
#Tag(name = "foo")
#SecurityRequirement(name = "custom")
public class SystemSigController {
#GetMapping(path = "/foo/{id}")
String getFoo(#PathVariable String id) {
...
}
}
The #SecurityRequirement.name value must be the same as #SecurityScheme.name.
Since you have #SecurityScheme(..., name = "access_token"...), the controller must use:
#SecurityRequirement(name = "access_token")

Customer booking API - Create bookingCustomer

When creating a new bookingCustomer object the displayName property value is replaced by emailAddress value:
POST https://graph.microsoft.com/beta/bookingBusinesses/Contosolunchdelivery#M365B489948.onmicrosoft.com/customers
Content-type: application/json
{
"displayName": "Joni Sherman",
"emailAddress": "jonis#relecloud.com"
}
Response:
HTTP/1.1 201 Created
Content-type: application/json
{
"#odata.context": "https://graph.microsoft.com/beta/$metadata#bookingBusinesses('Contosolunchdelivery%40M365B489948.onmicrosoft.com')/customers/$entity",
"id": "36038f36-634e-44e4-9415-d7d59c2347aa",
"displayName": "jonis#relecloud.com",
"emailAddress": "jonis#relecloud.com"
}
The example from docs (https://learn.microsoft.com/en-us/graph/api/bookingbusiness-post-customers?view=graph-rest-beta&tabs=http):
Same story over HTTP and SDK.
Any toughts?

Integration test with rest assured and basic auth with CSRF

I have a working Spring Boot with AngularJs app based on https://spring.io/guides/tutorials/spring-security-and-angular-js/
I am using Basic Authentication and I would like to write an integration test for it. Currently, I always get a 403 status code with the message:
Expected CSRF token not found. Has your session expired?
This is my test:
#Test
public void givenAdmin_deleteOfBookIsAllowed() {
Response response = given().auth().preemptive().basic("admin", "admin").get("/api/user/");
response.then().log().all();
String sessionId = response.sessionId();
String token = response.cookie("XSRF-TOKEN");
Book book = Books.randomBook();
bookRepository.save(book);
given()
.sessionId(sessionId)
.cookie("XSRF-TOKEN", token)
.header("X-XSRF-TOKEN", token)
.pathParam("id", book.getId().getId())
.when().delete("/api/books/{id}")
.then().statusCode(HttpStatus.SC_NO_CONTENT);
}
I am using a custom token repo per the tutorial:
private CsrfTokenRepository csrfTokenRepository() {
HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
repository.setHeaderName("X-XSRF-TOKEN");
return repository;
}
This is the request/response from the first call:
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Set-Cookie: JSESSIONID=7020E58A8D6DC2C883FD5D6BD086512A; Path=/; HttpOnly
Set-Cookie: XSRF-TOKEN=6c44ae09-73f9-4115-bbbf-b01773ec1b91; Path=/
X-Application-Context: application:staging:0
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Content-Encoding: gzip
Vary: Accept-Encoding
Date: Fri, 08 Jan 2016 07:43:21 GMT
{
"details": {
"remoteAddress": "127.0.0.1",
"sessionId": "7020E58A8D6DC2C883FD5D6BD086512A"
},
"authorities": [
{
"authority": "ROLE_ADMIN"
},
{
"authority": "ROLE_USER"
}
],
"authenticated": true,
"principal": {
"password": null,
"username": "admin",
"authorities": [
{
"authority": "ROLE_ADMIN"
},
{
"authority": "ROLE_USER"
}
],
"accountNonExpired": true,
"accountNonLocked": true,
"credentialsNonExpired": true,
"enabled": true
},
"credentials": null,
"name": "admin"
}
And from the second call:
Request method: DELETE
Request path: http://localhost:64588/api/books/04ad6d12-9b59-4ade-9a8a-e45daccb1f61
Proxy: <none>
Request params: <none>
Query params: <none>
Form params: <none>
Path params: id=04ad6d12-9b59-4ade-9a8a-e45daccb1f61
Multiparts: <none>
Headers: X-XSRF-TOKEN=6c44ae09-73f9-4115-bbbf-b01773ec1b91
Accept=*/*
Cookies: JSESSIONID=7020E58A8D6DC2C883FD5D6BD086512A
XSRF-TOKEN=6c44ae09-73f9-4115-bbbf-b01773ec1b91
Body: <none>
HTTP/1.1 403 Forbidden
Server: Apache-Coyote/1.1
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Content-Encoding: gzip
Vary: Accept-Encoding
Date: Fri, 08 Jan 2016 07:44:21 GMT
{
"timestamp": "2016-01-08T07:44:21.963+0000",
"status": 403,
"error": "Forbidden",
"message": "Expected CSRF token not found. Has your session expired?",
"path": "/api/books/04ad6d12-9b59-4ade-9a8a-e45daccb1f61"
}
You need to do 2 GET before post to use spring security CSRF protection in your rest client or integration test.
Make a GET request to login. This will return the JSESSIONID token and XSRF-TOKEN tokens. If you use the returned XSRF-TOKEN to POST it will fail, because we got it using empty/false JSESSIONID.
Get a useful XSRF-TOKEN from the second GET, using JSESSIONID from previous request.
Now you can use XSRF-TOKEN for your POST.
Example integration test for rest assured with basic auth and CSRF protection:
//1) get sessionId
Response response =
given().auth().preemptive().basic(userName, userPassword).contentType(JSON).
when().get(PREFIX_URL + "/user").
then().log().all().extract().response();
String jsessionidId = response.getSessionId();//or response.cookie("JSESSIONID");
//2) get XSRF-TOKEN using new/real sessionId
response =
given().
sessionId(jsessionidId).//or cookie("JSESSIONID", jsessionidId).
contentType(JSON).
when().get(PREFIX_URL + "/user").
then().log().all().extract().response();
//3) post data using XSRF-TOKEN
given().log().all().
sessionId(jsessionidId).//or cookie("JSESSIONID", jsessionidId).
header("X-XSRF-TOKEN", response.cookie("XSRF-TOKEN")).
queryParam("param",paramValue)).
body(someData).
contentType(JSON).
when().
post(PREFIX_URL + "/post_some_data").
then().
log().all().assertThat().statusCode(200);

EmberJS simpleauth with devise authorizer not sending token header

I can't figure out how to get ember-simple-auth with ember-simple-auth-devise authorizer (version 0.7.2) to append the token header to my backend requests.
Here is how my config/environment.js looks:
var ENV = {
modulePrefix: 'frontend-app',
environment: environment,
baseURL: '/',
locationType: 'auto',
EmberENV: {
FEATURES: {}
},
APP: {},
'simple-auth': {
authenticationRoute: 'sign-in',
authorizer: 'simple-auth-authorizer:devise'
}
};
if (environment === 'development') {
// ENV.APP.LOG_RESOLVER = true;
// ENV.APP.LOG_ACTIVE_GENERATION = true;
ENV.APP.LOG_TRANSITIONS = true;
// ENV.APP.LOG_TRANSITIONS_INTERNAL = true;
// ENV.APP.LOG_VIEW_LOOKUPS = true;
ENV.contentSecurityPolicy = {
'font-src': "'self' http://fonts.gstatic.com",
'style-src': "'self' 'unsafe-inline' http://fonts.googleapis.com",
'connect-src': "'self' http://localhost:3000",
};
ENV['simple-auth-devise'] = {
serverTokenEndpoint: "//localhost:3000/users/sign_in"
};
ENV.apiHost = "http://localhost:3000";
}
I was able to get authentication working, I get a session object in my application. Here are the headers from a successful exchange with the session endpoint:
POST /users/sign_in HTTP/1.1
Host: localhost:3000
Connection: keep-alive
Content-Length: 63
Accept: application/json, text/javascript
Origin: http://localhost:4200
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.89 Safari/537.36
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Referer: http://localhost:4200/sign-in
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.8
And the server responds with this session object:
{"user_token":"eo2xxzYsQ6UifMKvoLMF","user_id":3,"user_email":"snip#snip.com","user_first_name":"Xavier","user_last_name":"Lange"}
But all subsequent requests don't set the header:
GET /api/reports?user_id=3 HTTP/1.1
Host: localhost:3000
Connection: keep-alive
Cache-Control: max-age=0
Accept: application/json, text/javascript, */*; q=0.01
Origin: http://localhost:4200
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.89 Safari/537.36
Referer: http://localhost:4200/reports
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8
If-None-Match: W/"8e1b98de900ac194a0bc3ef6e9dc7459"
Where I should expect
Authorization: Token token="<token>", email="<email>"
to be in the headers sent to the backend. But it's not happening.
The issue was solved by checking the crossOriginWhitelist in development:
if (environment === 'development') {
// ENV.APP.LOG_RESOLVER = true;
// ENV.APP.LOG_ACTIVE_GENERATION = true;
ENV.APP.LOG_TRANSITIONS = true;
// ENV.APP.LOG_TRANSITIONS_INTERNAL = true;
// ENV.APP.LOG_VIEW_LOOKUPS = true;
ENV.contentSecurityPolicy = {
'font-src': "'self' http://fonts.gstatic.com",
'style-src': "'self' 'unsafe-inline' http://fonts.googleapis.com",
'connect-src': "'self' http://localhost:3000",
};
ENV['simple-auth']['crossOriginWhitelist'] = ['http://localhost:3000'];
ENV['simple-auth-devise']['serverTokenEndpoint'] = "//localhost:3000/users/sign_in"
ENV.apiHost = "http://localhost:3000";
}
Then I had to change simple-auth-devise to use email as the identification property (rails was having a hardtime parsing the Authorization header with authenticate_with_http_token:
var ENV = {
modulePrefix: 'my-app',
environment: environment,
baseURL: '/',
locationType: 'auto',
EmberENV: {
FEATURES: {
// Here you can enable experimental features on an ember canary build
// e.g. 'with-controller': true
}
},
APP: {
// Here you can pass flags/options to your application instance
// when it is created
},
'simple-auth': {
authenticationRoute: 'sign-in',
authorizer: 'simple-auth-authorizer:devise'
},
'simple-auth-devise': {
identificationAttributeName: 'email'
}
};
And my session serializer had to sync up with that attribute name:
class SessionsController < Devise::SessionsController
skip_before_action :authenticate_user_from_token!, only: [:create]
def create
respond_to do |format|
format.json do
self.resource = warden.authenticate!(auth_options)
sign_in(resource_name, resource)
data = {
token: self.resource.authentication_token,
user_id: self.resource.id,
email: self.resource.email,
user_first_name: self.resource.first_name,
user_last_name: self.resource.last_name
}
render json: data, status: 201
end
end
end
end

ios google+ api youtube subscribe to a channel

I am trying to subscribe a user to a youtube channel. So the user logs in to an iOS app, hits subscribe, and I would subscribe the user to a predefined channel.
So I have an app created with the YouTube Data API v3 service enabled.
scope: kGTLAuthScopeYouTube
GTLServiceYouTube *service = self.youTubeService;
GTLYouTubeSubscriptionSnippet* snippet = [GTLYouTubeSubscriptionSnippet object];
snippet.channelId = #"UCGRjJrpD2bmk9Ilq6nq80qg";
GTLYouTubeSubscription* subscription = [GTLYouTubeSubscription object];
subscription.snippet = snippet;
GTLQueryYouTube *query = [GTLQueryYouTube queryForSubscriptionsInsertWithObject:subscription part:#"contentDetails,snippet"];
[service executeQuery:query
completionHandler:^(GTLServiceTicket *ticket,
GTLYouTubeSubscription *subscription,
NSError *error) {
/* Callback */
NSLog(#"subscription:%#", subscription);
NSLog(#"error:%#", error);
}];
the server returns:
error:Error Domain=com.google.GTLJSONRPCErrorDomain Code=-32500 "The operation couldn’t be completed. (Required)" UserInfo=0x1005d3c20 {error=Required, NSLocalizedFailureReason=(Required), GTLStructuredError=GTLErrorObject 0x10054faa0: {message:"Required" data:[1] code:-32500}}
Any ideas why would this happen?
I am using the sample youtube app.
The sample url on https://developers.google.com/youtube/v3/docs/subscriptions/insert#examples
works correctly and from what I can tell I am doing the same thing above in objc code.
POST https://www.googleapis.com/youtube/v3/subscriptions?part=contentDetails%2Csnippet&fields=snippet&key={YOUR_API_KEY}
Content-Type: application/json
Authorization: Bearer ya29.AHES6ZQSBniofZhyVX4kfCn0-gVKKeiGayMcQHjTfWxMyffndRus7w
X-JavaScript-User-Agent: Google APIs Explorer
{
"snippet": {
"resourceId": {
"channelId": "UCDPM_n1atn2ijUwHd0NNRQw"
}
}
}
200 OK
Hide headers -
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Content-Encoding: gzip
Content-Length: 286
Content-Type: application/json; charset=UTF-8
Date: Fri, 14 Jun 2013 11:28:26 GMT
Etag: "2vd4g3cVsHAtTjJSdUMaBo1PBVE/Rv5ixqWURoAy7lbp6z3jpkm7IOE"
Expires: Fri, 01 Jan 1990 00:00:00 GMT
Pragma: no-cache
Server: GSE
{
"snippet": {
"publishedAt": "2013-06-14T11:28:26.000Z",
"title": "ColdplayVEVO",
"description": "",
"resourceId": {
"kind": "youtube#channel",
"channelId": "UCDPM_n1atn2ijUwHd0NNRQw"
},
"channelId": "UCQIKfhQEozSerNr3go189mw",
"thumbnails": {
"default": {
"url": "https://i1.ytimg.com/i/DPM_n1atn2ijUwHd0NNRQw/1.jpg?v=c2f0dd"
},
"high": {
"url": "https://i1.ytimg.com/i/DPM_n1atn2ijUwHd0NNRQw/mq1.jpg?v=c2f0dd"
}
}
}
}
UPDATE 1
Enabled loggin - see details below
youtube.subscriptions.insert
2013-06-19 08:45:52 +0000
Request: POST https://www.googleapis.com/rpc?prettyPrint=false
Request headers:
Accept: application/json-rpc
Authorization: Bearer _snip_
Cache-Control: no-cache
Content-Type: application/json-rpc; charset=utf-8
User-Agent: com.boxonline.tpp.bol.think/1.0 google-api-objc-client/2.0 MacOSX/10.8.3 (gzip)
Request body: (214 bytes)
{
"jsonrpc" : "2.0",
"method" : "youtube.subscriptions.insert",
"id" : "gtl_1",
"params" : {
"fields" : "snippet",
"part" : "contentDetails,snippet",
"resource" : {
"snippet" : {
"channelId" : "UCGRjJrpD2bmk9Ilq6nq80qg"
}
}
},
"apiVersion" : "v3"
}
Response: status 200
Response headers:
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Content-Encoding: gzip
Content-Length: 131
Content-Type: application/json; charset=UTF-8
Date: Wed, 19 Jun 2013 08:45:52 GMT
Expires: Fri, 01 Jan 1990 00:00:00 GMT
Pragma: no-cache
Server: GSE
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block
Response body: (152 bytes)
{
"error" : {
"message" : "Required",
"data" : [
{
"reason" : "publisherRequired",
"message" : "Required",
"domain" : "youtube.subscription"
}
],
"code" : -32500
},
"id" : "gtl_1"
}
The solution was to replace:
GTLYouTubeSubscriptionSnippet* snippet = [GTLYouTubeSubscriptionSnippet object];
snippet.channelId = #"UCGRjJrpD2bmk9Ilq6nq80qg";
with this:
GTLYouTubeSubscriptionSnippet* snippet = [GTLYouTubeSubscriptionSnippet object];
GTLYouTubeResourceId* resourceObject = [GTLYouTubeResourceId object];
resourceObject.channelId = #"UCGRjJrpD2bmk9Ilq6nq80qg";

Resources