Google Sheets API ERROR PERMISSION_DENIED - ruby

I'm trying to create sheets dynamically, but when running the code below I'm getting this error.
Code:
require "google/apis/sheets_v4"
require "googleauth"
require "googleauth/stores/file_token_store"
require "fileutils"
OOB_URI = "urn:ietf:wg:oauth:2.0:oob".freeze
APPLICATION_NAME = "Google Sheets API Ruby Quickstart".freeze
CREDENTIALS_PATH = "new_credential.json".freeze
# The file token.yaml stores the user's access and refresh tokens, and is
# created automatically when the authorization flow completes for the first
# time.
TOKEN_PATH = "token.yaml".freeze
SCOPE = Google::Apis::SheetsV4::AUTH_SPREADSHEETS_READONLY
##
# Ensure valid credentials, either by restoring from the saved credentials
# files or intitiating an OAuth2 authorization. If authorization is required,
# the user's default browser will be launched to approve the request.
#
# #return [Google::Auth::UserRefreshCredentials] OAuth2 credentials
def authorize
client_id = Google::Auth::ClientId.from_file CREDENTIALS_PATH
token_store = Google::Auth::Stores::FileTokenStore.new file: TOKEN_PATH
authorizer = Google::Auth::UserAuthorizer.new client_id, SCOPE, token_store
user_id = "default"
credentials = authorizer.get_credentials user_id
if credentials.nil?
url = authorizer.get_authorization_url base_url: OOB_URI
puts "Open the following URL in the browser and enter the " \
"resulting code after authorization:\n" + url
code = gets
credentials = authorizer.get_and_store_credentials_from_code(
user_id: user_id, code: code, base_url: OOB_URI
)
end
credentials
end
# Initialize the API
service = Google::Apis::SheetsV4::SheetsService.new
service.client_options.application_name = APPLICATION_NAME
service.authorization = authorize
spreadsheet = {
properties: {
title: 'Sales Report'
}
}
spreadsheet = service.create_spreadsheet(spreadsheet,
fields: 'spreadsheetId')
puts "Spreadsheet ID: #{spreadsheet.spreadsheet_id}"
Error:
Traceback (most recent call last):
15: from quickstart.rb:49:in `<main>'
14: from /home/vagrant/.rvm/gems/ruby-2.7.2/gems/google-api-client-0.53.0/generated/google/apis/sheets_v4/service.rb:121:in `create_spreadsheet'
13: from /home/vagrant/.rvm/gems/ruby-2.7.2/gems/google-apis-core-0.4.2/lib/google/apis/core/base_service.rb:377:in `execute_or_queue_command'
12: from /home/vagrant/.rvm/gems/ruby-2.7.2/gems/google-apis-core-0.4.2/lib/google/apis/core/http_command.rb:102:in `execute'
11: from /home/vagrant/.rvm/gems/ruby-2.7.2/gems/retriable-3.1.2/lib/retriable.rb:56:in `retriable'
10: from /home/vagrant/.rvm/gems/ruby-2.7.2/gems/retriable-3.1.2/lib/retriable.rb:56:in `times'
9: from /home/vagrant/.rvm/gems/ruby-2.7.2/gems/retriable-3.1.2/lib/retriable.rb:61:in `block in retriable'
8: from /home/vagrant/.rvm/gems/ruby-2.7.2/gems/google-apis-core-0.4.2/lib/google/apis/core/http_command.rb:111:in `block in execute'
7: from /home/vagrant/.rvm/gems/ruby-2.7.2/gems/retriable-3.1.2/lib/retriable.rb:56:in `retriable'
6: from /home/vagrant/.rvm/gems/ruby-2.7.2/gems/retriable-3.1.2/lib/retriable.rb:56:in `times'
5: from /home/vagrant/.rvm/gems/ruby-2.7.2/gems/retriable-3.1.2/lib/retriable.rb:61:in `block in retriable'
4: from /home/vagrant/.rvm/gems/ruby-2.7.2/gems/google-apis-core-0.4.2/lib/google/apis/core/http_command.rb:114:in `block (2 levels) in execute'
3: from /home/vagrant/.rvm/gems/ruby-2.7.2/gems/google-apis-core-0.4.2/lib/google/apis/core/http_command.rb:311:in `execute_once'
2: from /home/vagrant/.rvm/gems/ruby-2.7.2/gems/google-apis-core-0.4.2/lib/google/apis/core/http_command.rb:195:in `process_response'
1: from /home/vagrant/.rvm/gems/ruby-2.7.2/gems/google-apis-core-0.4.2/lib/google/apis/core/api_command.rb:134:in `check_status'
/home/vagrant/.rvm/gems/ruby-2.7.2/gems/google-apis-core-0.4.2/lib/google/apis/core/http_command.rb:229:in `check_status': PERMISSION_DENIED: Request had insufficient authentication scopes. (Google::Apis::ClientError)
For this code I am using OAUTH authentication and generating the JSON
I selected all available scopes, recreated the JSON authentication, but it still doesn't work.
What can I do?

Mate, by using this line, you're telling the app to only get READONLY permission:
Google::Apis::SheetsV4::AUTH_SPREADSHEETS_READONLY
But in this lines I think you're trying to create a speadsheet
spreadsheet = {
properties: {
title: 'Sales Report'
}
}
spreadsheet = service.create_spreadsheet(spreadsheet,
fields: 'spreadsheetId')
Solution:
Edit the scope to:
SCOPE = Google::Apis::SheetsV4::AUTH_SPREADSHEETS
Hope this help

As per the documentation for this 'create' request and the error you are getting that is an issue directly related to the scopes you are using in your code.
These are the scopes you need to add to your script and your project scopes:
https://www.googleapis.com/auth/drive
https://www.googleapis.com/auth/drive.file
https://www.googleapis.com/auth/spreadsheets
After you have done the modification here:
SCOPE = Google::Apis::SheetsV4::AUTH_SPREADSHEETS_READONLY
Execute the code and issue will be solved.

Related

The "realmRoles" parameter is ignored when adding a user via the Keycloak API

I am trying to create a user via the Keycloak API, and I would like to assign a realm-level role to them when they are first added. However, it doesn't seem to work like the documentation says it should.
I know that I could simply make a second add-role-to-user API request after the initial create-user one, but:
The documentation indicates that I shouldn't need to do this.
The second API request could fail, leaving the user in an "incomplete" state.
It would make the code I'm writing more complex than it needs to be.
To test this in irb, using the keycloak Ruby gem, I first request an access token from Keycloak:
require 'keycloak'
json = Keycloak::Client.get_token_by_client_credentials
access_token = JSON.parse(json)['access_token']
All of the following create a user within Keycloak, but without the "owner" role:
Keycloak::Admin.generic_post('users', nil, { username: 'someone', realmRoles: ['owner'] }, access_token)
Keycloak::Admin.generic_post('users', nil, { username: 'someone', realmRoles: ['1fff5f5f-7357-4f73-b45d-65ccd01f3bc8'] }, access_token)
Keycloak::Admin.generic_post('users', nil, { username: 'someone', realmRoles: ['{"id":"1fff5f5f-7357-4f73-b45d-65ccd01f3bc8","name":"owner","description":"Indicates that a user is the owner of an organisation.","composite":false,"clientRole":false,"containerId":"MyRealmName"}'] }, access_token)
Attempting to use a role-hash instead of a string causes an error:
Keycloak::Admin.generic_post('users', nil, { username: 'someone', realmRoles: [{"id"=>"1fff5f5f-7357-4f73-b45d-65ccd01f3bc8", "name"=>"owner", "description"=>"Indicates that a user is the owner of an organisation.", "composite"=>false, "clientRole"=>false, "containerId"=>"MyRealmName"}] }, access_token)
Traceback (most recent call last):
16: from /home/thomas/.rvm/rubies/ruby-2.6.3/lib/ruby/gems/2.6.0/gems/irb-1.0.0/exe/irb:11:in `<top (required)>'
15: from (irb):8
14: from /home/thomas/.rvm/gems/ruby-2.6.3/gems/keycloak-3.0.0/lib/keycloak.rb:541:in `generic_post'
13: from /home/thomas/.rvm/gems/ruby-2.6.3/gems/keycloak-3.0.0/lib/keycloak.rb:943:in `generic_request'
12: from /home/thomas/.rvm/gems/ruby-2.6.3/gems/keycloak-3.0.0/lib/keycloak.rb:915:in `block in generic_request'
11: from /home/thomas/.rvm/gems/ruby-2.6.3/gems/rest-client-2.0.2/lib/restclient.rb:71:in `post'
10: from /home/thomas/.rvm/gems/ruby-2.6.3/gems/rest-client-2.0.2/lib/restclient/request.rb:52:in `execute'
9: from /home/thomas/.rvm/gems/ruby-2.6.3/gems/rest-client-2.0.2/lib/restclient/request.rb:145:in `execute'
8: from /home/thomas/.rvm/gems/ruby-2.6.3/gems/rest-client-2.0.2/lib/restclient/request.rb:715:in `transmit'
7: from /home/thomas/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/net/http.rb:920:in `start'
6: from /home/thomas/.rvm/gems/ruby-2.6.3/gems/rest-client-2.0.2/lib/restclient/request.rb:725:in `block in transmit'
5: from /home/thomas/.rvm/gems/ruby-2.6.3/gems/rest-client-2.0.2/lib/restclient/request.rb:807:in `process_result'
4: from /home/thomas/.rvm/gems/ruby-2.6.3/gems/keycloak-3.0.0/lib/keycloak.rb:916:in `block (2 levels) in generic_request'
3: from /home/thomas/.rvm/gems/ruby-2.6.3/gems/keycloak-3.0.0/lib/keycloak.rb:958:in `rescue_response'
2: from /home/thomas/.rvm/gems/ruby-2.6.3/gems/rest-client-2.0.2/lib/restclient/abstract_response.rb:103:in `return!'
1: from /home/thomas/.rvm/gems/ruby-2.6.3/gems/rest-client-2.0.2/lib/restclient/abstract_response.rb:223:in `exception_with_response'
RestClient::InternalServerError (500 Internal Server Error)
Keycloak prints the following, indicating that - as expected - the roles should be an array of strings, not hashes:
08:53:27,889 ERROR [org.keycloak.services.error.KeycloakErrorHandler] (default task-22) Uncaught server error: com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `java.lang.String` out of START_OBJECT token
at [Source: (io.undertow.servlet.spec.ServletInputStreamImpl); line: 1, column: 37] (through reference chain: org.keycloak.representations.idm.UserRepresentation["realmRoles"]->java.util.ArrayList[0])
The same thing happens if I pass a single string instead of an array, like:
Keycloak::Admin.generic_post('users', nil, { username: 'someone', realmRoles: 'owner' }, access_token)
Am I doing something wrong, or is this simply a bug in the Keycloak API?
Reference
https://www.keycloak.org/docs-api/9.0/rest-api/index.html#_createuser
https://www.keycloak.org/docs-api/9.0/rest-api/index.html#_userrepresentation
Similar questions
Keycloak : unable to map user roles when creating user for api
Keycloak: roles not assigned when user is created via CLI
You did nothing wrong. It is a bug in the Keycloak API.
This request should work:
Keycloak::Admin.generic_post('users', nil, { username: 'someone', realmRoles: ['owner'] }, access_token)
Unfortunately the API documentation is wrong because the 'realmRoles' attribute doesn't work when trying to create/update a user/group.
You can find more informations about the behavior on the official bug tracker of Keycloak :
https://issues.jboss.org/browse/KEYCLOAK-3410
https://issues.jboss.org/browse/KEYCLOAK-10876
https://issues.jboss.org/browse/KEYCLOAK-5038
...
For now the only solution is to make multiple requests on the API, using the RoleMappers to map a role to a user.
Documentation about those operations : https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_role_mapper_resource
The bug is still present in keycloak 19.0.1 even tho was reported in 2016.
So there is a work around as GoGusto suggested. First create the user and then add the roles to the user.
private void addRealmRoleToUser(String username, String role){
UserRepresentation userRepresentation = keycloak.realm(REALM_NAME).users().search(username).get(0);
UserResource userResource =
keycloak.realm(REALM_NAME).users().get(userRepresentation.getId());
List<RoleRepresentation> rolesToAdd =
Arrays.asList(keycloak.realm(REALM_NAME).roles().get(TEST_ROLE).toRepresentation());
userResource.roles().realmLevel().add(rolesToAdd);
}
Respectively userRepresentation.getRealmRoles() is not working as well. getRealmRoles has to be done with UserResouce class like its written in keycloak UserTest.java:
RoleMappingResource userRoles = realm.users().get(userId).roles();
userRoles.realmLevel().add(Collections.singletonList(realm.roles().get("realm-composite").toRepresentation()));
userRoles.clientLevel(clientUuid).add(Collections
.singletonList(realm.clients().get(clientUuid).roles().get("client-composite").toRepresentation()));
// check state before making the direct assignments
assertNames(userRoles.realmLevel().listAll(), "realm-composite", Constants.DEFAULT_ROLES_ROLE_PREFIX + "-test");
assertNames(userRoles.realmLevel().listAvailable(), "realm-child", "realm-role-in-group",
"admin", "customer-user-premium", "realm-composite-role",
"sample-realm-role",
"attribute-role", "user", "offline_access", Constants.AUTHZ_UMA_AUTHORIZATION);
assertNames(userRoles.realmLevel().listEffective(), "realm-composite", "realm-child", "realm-role-in-group",
"user", "offline_access", Constants.AUTHZ_UMA_AUTHORIZATION,
Constants.DEFAULT_ROLES_ROLE_PREFIX + "-test");

Connect to active directory through LDAP-Ruby

I'm trying to connect to an AD instance from my Ruby application. I've chosen LDAP for the job.
Below is my connection settings and script I've written.
def name_for_login( email, password )
email = email[/\A\w+/].downcase # Throw out the domain, if it was there
email << "#example.com" # I only check people in my company
ldap = Net::LDAP.new(
host: '10.0.0.2',
port: 1027,
auth: { method: :simple, email: email, password:password }
)
if ldap.bind
p 'lol'
# Yay, the login credentials were valid!
# Get the user's full name and return it
ldap.search(
base: "OU=Users,OU=Accounts,DC=example,DC=com",
filter: Net::LDAP::Filter.eq( "mail", email ),
attributes: %w[ displayName ],
return_result:true
).first.displayName.first
end
end
and test credentials:
windows : Windows#test
tester : Pass#123
If I run the script, it throws me the following error :
irb(main):025:0> name_for_login('tester','Pass#123')
Net::LDAP::BindingInformationInvalidError: Invalid binding information
from /Library/Ruby/Gems/2.3.0/gems/net-ldap-0.16.1/lib/net/ldap/auth_adapter/simple.rb:14:in `bind'
from /Library/Ruby/Gems/2.3.0/gems/net-ldap-0.16.1/lib/net/ldap/connection.rb:278:in `block in bind'
from /Library/Ruby/Gems/2.3.0/gems/net-ldap-0.16.1/lib/net/ldap/instrumentation.rb:19:in `instrument'
from /Library/Ruby/Gems/2.3.0/gems/net-ldap-0.16.1/lib/net/ldap/connection.rb:275:in `bind'
from /Library/Ruby/Gems/2.3.0/gems/net-ldap-0.16.1/lib/net/ldap.rb:868:in `block in bind'
from /Library/Ruby/Gems/2.3.0/gems/net-ldap-0.16.1/lib/net/ldap/instrumentation.rb:19:in `instrument'
from /Library/Ruby/Gems/2.3.0/gems/net-ldap-0.16.1/lib/net/ldap.rb:860:in `bind'
from (irb):9:in `name_for_login'
from (irb):25
from /usr/bin/irb:11:in `<main>'
I'm unsure from where to troubleshoot to understand the problem.
The AD is on a Windows server hosted on Azure.
I don't know Ruby, but my guess is that the problem is here:
auth: { method: :simple, email: email, password:password }
According to the documentation, you should be using the username property, not email. And you need to set it to either the username (sAMAccountName) of the account, or the userPrincipalName (which may be the same as the email address, or the distinguishedName.
Assuming the userPrincipalName is the same as the email address, then this might work:
auth: { method: :simple, username: email, password:password }

Error after trying to access xml (Zlib::BufError) (Ruby)

I keep getting and error whenever I try and access an XML file in Ruby. This is my code:
require 'rubygems'
require 'net/http'
require 'uri'
require 'json'
require 'open-uri'
url = 'http://access.alchemyapi.com/calls'
service = '/text/TextGetRankedTaxonomy'
apikey = '?apikey=4317fce9281094613deee9ebcc5aaf5238cd0748'
thething = '&text='
text = 'men%27s%20white%20crew%20neck%20shirt'
fullurl = url + service + apikey + thething + text
opener = open(fullurl) {|f| f.read }
Here is the error:
C:/Ruby21/lib/ruby/2.1.0/net/http/response.rb:357:in `finish': buffer error (Zlib::BufError)
from C:/Ruby21/lib/ruby/2.1.0/net/http/response.rb:357:in `finish'
from C:/Ruby21/lib/ruby/2.1.0/net/http/response.rb:262:in `ensure in inflater'
from C:/Ruby21/lib/ruby/2.1.0/net/http/response.rb:262:in `inflater'
from C:/Ruby21/lib/ruby/2.1.0/net/http/response.rb:274:in `read_body_0'
from C:/Ruby21/lib/ruby/2.1.0/net/http/response.rb:201:in `read_body'
from C:/Ruby21/lib/ruby/2.1.0/net/http/response.rb:226:in `body'
from C:/Ruby21/lib/ruby/2.1.0/net/http/response.rb:163:in `reading_body'
from C:/Ruby21/lib/ruby/2.1.0/net/http.rb:1420:in `block in transport_request'
from C:/Ruby21/lib/ruby/2.1.0/net/http.rb:1411:in `catch'
from C:/Ruby21/lib/ruby/2.1.0/net/http.rb:1411:in `transport_request'
from C:/Ruby21/lib/ruby/2.1.0/net/http.rb:1384:in `request'
from C:/Ruby21/lib/ruby/2.1.0/net/http.rb:509:in `block in post_form'
from C:/Ruby21/lib/ruby/2.1.0/net/http.rb:853:in `start'
from C:/Ruby21/lib/ruby/2.1.0/net/http.rb:583:in `start'
from C:/Ruby21/lib/ruby/2.1.0/net/http.rb:507:in `post_form'
from C:/Users/KVadher/Desktop/test151:11:in `<main>'
Is there anything I can do to either solve the error or run past it?
There is something wrong with the way this API server is encoding the data. To bypass this, you can simple say in the HTTP header that you don't accept any kind of encoding:
opener = open(fullurl, 'Accept-Encoding' => '') {|f| f.read }

404 Resource Not Found: domain with Google Directory API

I followed the quick start and am attempting to create a user using the google-api-ruby-client.
I've set up access in the google api console. And I can get this to work using the API explorer.
But when I try using the ruby client, I'm getting a resource not found: domain error.
Here's the code:
def self.create_user
# Initialize the client.
client = Google::APIClient.new(
:application_name => 'MYAPP',
:application_version => '0.0.1'
)
# Authorization
# Load our credentials for the service account
key = Google::APIClient::KeyUtils.load_from_pkcs12(KEY_FILE, KEY_SECRET)
client.authorization = Signet::OAuth2::Client.new(
token_credential_uri: 'https://accounts.google.com/o/oauth2/token',
audience: 'https://accounts.google.com/o/oauth2/token',
scope: 'https://www.googleapis.com/auth/admin.directory.user',
issuer: ACCOUNT_ID,
signing_key: key)
# Request a token for our service account
client.authorization.fetch_access_token!
# Load API Methods
admin = client.discovered_api('admin', 'directory_v1')
# Make an API call.
result = client.execute(
admin.users.update,
name: { familyName: 'testy', givenName: 'testerson' },
password: '!password12345!',
primaryEmail: 'ttesterson#my-actual-domain.com'
)
result.data
end
Here's the response:
"error"=>{"errors"=>[{"domain"=>"global", "reason"=>"notFound", "message"=>"Resource Not Found: domain"}], "code"=>404, "message"=>"Resource Not Found: domain"}
Why?
After a bit of documentation reading, there were two things that I needed to fix.
I hadn't set up the proper authorization for my test service account.
You have to go to the Apps Console > Security > Advanced > Manage API client access and add the client url for your service account as well as any specific permissions that you want to add
As seen in this question, it seems that you need to create a user object rather than just passing in parameters.
Here's my updated code:
# Authorization happens here ....
api = client.discovered_api('admin', 'directory_v1')
new_user = api.users.insert.request_schema.new(
name: { familyName: 'Testy', givenName: 'Testerson' },
primaryEmail: 'ttttesterson#<domain-redacted>.com',
password: 'password123'
)
result = client.execute(
api_method: api.users.insert,
body_object: new_user
)

Obtaining a render Argument Error: What is the correct syntax or usage?

I am new to Ruby, Sinatra and Pusher so this is a basic question. I am attempting to authenticate a Pusher Client (using iOS demo code https://github.com/lukeredpath/libPusher). The server code below fails with error when the iOS client attempts to join a presence channel:
ArgumentError - wrong number of arguments (1 for 2):
/Users/waveocean/.rvm/gems/ruby-1.9.3-p327/gems/sinatra-1.3.3/lib/sinatra/base.rb:665:in `render'
web.rb:13:in `auth'
web.rb:26:in `block in <main>'
/Users/waveocean/.rvm/gems/ruby-1.9.3-p327/gems/sinatra-1.3.3/lib/sinatra/base.rb:1265:in `call'
... snipped for brevity ...
Here is the code:
require 'sinatra'
require 'pusher'
require 'thin'
Thin::HTTP_STATUS_CODES[403] = "FORBIDDEN"
Pusher.app_id = 'MY-APP-ID'
Pusher.key = 'MY-KEY'
Pusher.secret = 'MY-SECRET'
def auth
response = Pusher[params[:channel_name]].authenticate(params[:socket_id], {:user_id => 101})
render :json => response
end
use Rack::Auth::Basic, "Protected Area" do |username, password|
username == 'foo' && password == 'bar'
end
post '/presence/auth' do
if params[:channel_name] == 'presence-demo'
auth
else
# render :text => "Forbidden", :status => '403'
response.status = 403
end
end
Can someone provide a suggestion or correct usage of render?
Here's is what I discovered. render is associated with Rails, and not strictly Ruby. To respond to the Sinatra route, use the following in the auth method:
def auth
response = Pusher[params[:channel_name]].authenticate(params[:socket_id], {:user_id => 101})
[200, {"Content-Type" => "application/json"}, response.to_json]
end
As it turns out, the Pusher iOS project demo provides a Scripts/auth_server.rb file with the required implementation. It is documented with the installation instructions here: https://github.com/lukeredpath/libPusher/wiki/Adding-libPusher-to-your-project .

Resources