I can't create posix attributes on existing account in admin.google.com (also known as Google Cloud Identity / Google Directory) using Admin SDK (Directory API).
To explain my issue, I will use the API tester : https://developers.google.com/admin-sdk/directory/v1/reference/users/update?apix=true
I use the update function to update an existing account without POSIX attributes.
To do that I copy the request body below and use request key : testmdr#contoso.com :
{
"posixAccounts": [
{
"username": "testmdr_contoso_com",
"uid": "2147483645", # I use id between 65535 and 2147483647 (explain: in google documentation)
"gid": "1001",
"homeDirectory": "/home/testmdr_contoso.com",
"shell": "/bin/bash"
}
]
}
I obtain an 503 error :
{
"error": {
"errors": [
{
"domain": "global",
"reason": "backendError",
"message": "Service unavailable. Please try again"
}
],
"code": 503,
"message": "Service unavailable. Please try again"
}
}
If I update name or other, it works.
If I update existing POSIX attribute (existing because create when connection on GCE using OS Login functionality :Here), it works.
Please help me if it's limitation or bug
The requestKey should be the UUID of the user . . . There are probably better ways to do this, but you can get the username / name(requestKey/UUID) by querying the metadata on an oslogin-enabled instance, e.g. (first column is username, second column is requestKey for API tester):
curl -s "http://metadata.google.internal/computeMetadata/v1/oslogin/users?pagesize=50&pagetoken=0" -H "Metadata-Flavor: Google" | \
jq -r '.loginProfiles[]|.posixAccounts[].username,.name' | \
paste - -
(You may have to play with the pagesize & pagetoken parameters)
Related
Im trying to create user with external claims, but something wents wrong.
I added my claims to scim2-schema-extension.config, to external claims and local claims, but API's response for my request is:
{
"schemas": [
"urn:ietf:params:scim:api:messages:2.0:Error"
],
"detail": "Error in adding the user: testoviy22#mail.ru to the user store.",
"status": "500"
}
In logs i have
: ERR_13735_ELEMENT_FOR_OID_DOES_NOT_EXIST ATTRIBUTE_TYPE for OID comment does not exist!]; remaining name 'mail=testoviy22#mail.ru'
at java.naming/com.sun.jndi.ldap.LdapCtx.mapErrorCode(LdapCtx.java:3280)
at java.naming/com.sun.jndi.ldap.LdapCtx.processReturnCode(LdapCtx.java:3205)
at java.naming/com.sun.jndi.ldap.LdapCtx.processReturnCode(LdapCtx.java:2996)
at java.naming/com.sun.jndi.ldap.LdapCtx.c_bind(LdapCtx.java:452)
at java.naming/com.sun.jndi.toolkit.ctx.ComponentDirContext.p_bind(ComponentDirContext.java:299)
at java.naming/com.sun.jndi.toolkit.ctx.PartialCompositeDirContext.bind(PartialCompositeDirContext.java:217)
at org.wso2.carbon.user.core.ldap.UniqueIDReadWriteLDAPUserStoreManager.persistUser(UniqueIDReadWriteLDAPUserStoreManager.java:312)
... 73 more
Local claim
External claim
Request sample:
{
"name": {
"familyName": "Zubenko",
"givenName": "Michael"
},
"password": "qwerty",
"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User": {
"comment": "test"
},
"userName": "testoviy22#mail.ru"
}
Seems you are using an LDAP server as your primary userstore, and that server doesn't support the attribute named "comment"
Refer to the note in https://is.docs.wso2.com/en/latest/references/extend/provisioning/extend-scim2-user-schemas/#map-the-custom-claim
You can use the word "customClaim" (or any other preferred word) as
the Mapped Attribute only when using a JDBC userstore because JDBC
userstores will automatically create a new attribute if it does not
already exist in the user store. However, If you are using LDAP or
Active Directory, you will have to use an attribute that exists in the
user store already.
Change the mapped attribute of the local claim to some valid attribute in the LDAP schema
I have a google cloud function. Within this function, I want to write files to GCS (google cloud storage), then get a signed URL of the file that is written to GCS and send that URL to the caller.
For local development, I run the functions locally using the functions-framework command:
functions-framework --source=.build/ --target=http-function --port 8082
When I want to write to GCS or get the signed URL, the cloud functions framework just tries to get the credentials from the signed-in gcloud CLI user. However, I want to point it to read the credentials from a service account. For all other gcloud development purposes, we have put the service account information in a local creds.json file and point the gcloud to read from that file.
Is there any way I can achieve this for functions? Meaning that when I start the functions locally (using functions-framework), I point it to the creds.json file to read the credentials from there?
All Google's SDKs, e.g., for GCS, make use of Application Default Credentials which you should be using instead of explicitly pathing to a key. If this is true for functions-framework, then exporting the variable should work.
The command gcloud auth application-default login is a better recommendation in that case, especially for testing the signed URL, because with that local credential as well as the Cloud Functions credential (through metadata server), the private key isn't present, and the signed URL must be called in a specific manner (provide token and the service account to be able to sign the URL).
Using gcloud auth application-default login creates Application Default Credentials, which have all the powers of the user's account and are persisted as a key called {HOME}/.config/gcloud/application-default_credentials.
Cloud Function Local Development Authentication
This is what I'm doing for local development of a google cloud function using Nodejs that is triggered by a pub/sub event. This function reads a file from a google cloud storage. This uses the Functions Framework Nodejs
TL;DR;
# shell A
gcloud auth application-default login
npm start
pub/sub event message
# shell B
curl -d "#mockPubSub.json" \
-X POST \
-H "Content-Type: application/json" \
http://localhost:8080
Greater Details
Cloud Function with Functions Framework
Docs: Functions Framework Nodejs
package.json
note the --target and --signature-type
{
...
"scripts": {
"start": "npx functions-framework --target=helloPubSub --signature-type=http"
},
"dependencies": {
"#google-cloud/debug-agent": "^7.0.0",
"#google-cloud/storage": "^6.0.0"
},
"devDependencies": {
"#google-cloud/functions-framework": "^3.1.2"
}
...
}
sample nodejs cloud function that downloads file into memory
/* modified from the sample
index.js
*/
const {Storage} = require('#google-cloud/storage');
function log(message, severity = 'DEBUG', payload) {
// Structured logging
// https://cloud.google.com/functions/docs/monitoring/logging#writing_structured_logs
if (!!payload) {
// If payload is an Error, get the stack trace.
if (payload instanceof Error && !!payload.stack) {
if (!!message ) {
message = message + '\n' + payload.stack;
} else {
message = payload.stack;
}
}
}
const logEntry = {
message: message,
severity: severity,
payload : payload
};
console.log(JSON.stringify(logEntry));
}
function getConfigFile(payload){
console.log("Get Config File from GCS")
const bucketName = 'some-bucket-in-a-project';
const fileName = 'config.json';
// Creates a client
const storage = new Storage();
async function downloadIntoMemory() {
// Downloads the file into a buffer in memory.
const contents = await storage.bucket(bucketName).file(fileName).download();
console.log(
`Contents of gs://${bucketName}/${fileName} are ${contents.toString()}.`
);
}
downloadIntoMemory().catch(console.error);
}
exports.helloPubSub = async (pubSubEvent, context) => {
/*
Read payload from the event and log the exception in App project if the payload cannot be parsed
*/
try {
const payload = Buffer.from(pubSubEvent.body.message.data, 'base64').toString()
const pubSubEventObj = JSON.parse(payload) ;
console.log("name: ", pubSubEventObj.name);
getConfigFile(pubSubEventObj)
} catch (err) {
log('failed to process payload: + payload \n' , 'ERROR', err);
}
};
Mock Message for Pub/Sub Event
blog reference, but I'm not using the emulator
myJson.json
{"widget": {
"debug": "on",
"window": {
"title": "Sample Konfabulator Widget",
"name": "main_window",
"width": 500,
"height": 500
},
"image": {
"src": "Images/Sun.png",
"name": "sun1",
"hOffset": 250,
"vOffset": 250,
"alignment": "center"
},
"text": {
"data": "Click Here",
"size": 36,
"style": "bold",
"name": "text1",
"hOffset": 250,
"vOffset": 100,
"alignment": "center",
"onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;"
}
}}
encode for Pub/sub message ( likely there's a better way )
cat myJson.json | grep -v % | base64
take that output and put it into value for the data key:
mockPubSub.json
{
"message": {
"attributes": {
"greeting": "Hello from the Cloud Pub/Sub Emulator!"
},
"data": "< put the output of the base64 from above here >",
"messageId": "136969346945"
},
"subscription": "projects/myproject/subscriptions/mysubscription"
}
Follow steps from the TL;DR: above.
Disclaimers
gcloud auth application-default login uses the permissions of the user who executes the command. So remember, in production, the service account the cloud function uses will need to read from the storage bucket.
while scrubbing this (i.e. renaming bits) and copying it over, I may have messed it up. Sorry if that is true.
this is all contrived if you are curious, my design is to take a message from a cloud scheduler that includes relevant details about what to read from the config.
this article explains a way to hot-reload the cloud function
It remains unclear why I'm using ``-signature-type=http` to mock message, but for now, I am.
On Google Admin screen, I can get numbers of available licenses and used licenses shown below:
How can I get these numbers via API?
Note: I read this question and tried, but not worked well.
-- EDIT: 2021/07/15 --
My request:
https://developers.google.com/admin-sdk/reports/reference/rest/v1/customerUsageReports/get
date: (few days before now)
parameters: accounts:gsuite_unlimited_total_licenses (comes from Account Parameters)
Response from API:
{
"kind": "admin#reports#usageReports",
"etag": "\"occ7bTD-Q2yefKPIae3LMOtCT9xQVZYBzlAbHU5b86Q/gt9BLwRjoWowpJCRESV3vBMjYMc\""
}
Expectation: I want to get the data same as 2 available, 1132 assigned as the GUI shows.
To be honestly, I'm not satisfying even if I can get info via this API, because it seems not responding real-time data like GUI.
I think there are 2 ways this information can be obtain, but I can confirm for only one of them.
1. Using the Report API that you mentioned.
NOTE : The report is not live data, so you must run the API call with a "date" parameter set at least 2 days before the execution date
Given that, you would have to run this GET method with the proper date in the {date} param
GET https://admin.googleapis.com/admin/reports/v1/usage/dates/{date}
Then you would need to parse through the parameters to find the desired license you are looking for.
reference - https://developers.google.com/admin-sdk/reports/reference/rest/v1/customerUsageReports#UsageReports
Here is how it look like after parsing
[
{
"BoolValue": null,
"DatetimeValueRaw": null,
"DatetimeValue": null,
"IntValue": 12065,
"MsgValue": null,
"Name": "accounts:gsuite_enterprise_total_licenses",
"StringValue": null
},
{
"BoolValue": null,
"DatetimeValueRaw": null,
"DatetimeValue": null,
"IntValue": 12030,
"MsgValue": null,
"Name": "accounts:gsuite_enterprise_used_licenses",
"StringValue": null
}
]
Important : The repot will always date 2 day back, so you can get the total number of licenses gsuite_enterprise_total_licenses in my example, and then use the Enterprise License Manager API to retrieve all currently assigned licenses
reference https://developers.google.com/admin-sdk/licensing/reference/rest
2. Using the Reseller API
Retrieving the information from the reseller point of view you would need to use the subscriptions.get method, providing your customerId and subscriptionId , calling the following GET request:
GET https://reseller.googleapis.com/apps/reseller/v1/customers/{customerId}/subscriptions/{subscriptionId}
The response of that would be a subscriptions resource, that contains various information about the license and the Seats object , which if you expand looks like this :
{
"numberOfSeats": integer,
"maximumNumberOfSeats": integer,
"licensedNumberOfSeats": integer,
"kind": string
}
numberOfSeats should be the total amount of licenses and licensedNumberOfSeats should be the number of users having that license assigned to them.
NOTE : in order to use this API , the given tenant should have a "fully executed and signed reseller contract" - https://developers.google.com/admin-sdk/reseller/v1/how-tos/prerequisites
Reference - https://developers.google.com/admin-sdk/reseller/reference/rest/v1/subscriptions
Answer:
You can only get the number of assigned licenses using the API, the number available isn't exposed and so does not get returned.
More Information:
Given that you have licenses assigned for your domain, and the user that is querying the API has access to this information, you can retrieve the data with the following request:
curl \
'https://admin.googleapis.com/admin/reports/v1/usage/dates/2021-07-10?parameters=accounts%3Agsuite_unlimited_total_licenses&fields=*&key=[YOUR_API_KEY]' \
--header 'Authorization: Bearer [YOUR_ACCESS_TOKEN]' \
--header 'Accept: application/json' \
--compressed
While not necessary, I added the parameter field=* in order to make sure all data is returned.
This gave me a response as such:
{
"kind": "admin#reports#usageReports",
"etag": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"usageReports": [
{
"kind": "admin#reports#usageReport",
"date": "2021-07-10",
"etag": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"entity": {
"type": "CUSTOMER",
"customerId": "C0136mgul"
},
"parameters": [
{
"name": "accounts:gsuite_unlimited_total_licenses",
"intValue": "233"
}
]
}
]
}
Here you can see that the intValue for accounts:gsuite_unlimited_total_licenses is 233 - which is reflected in the UI:
Feature Request:
You can however let Google know that this is a feature that is important for access to their APIs, and that you would like to request they implement it.
Google's Issue Tracker is a place for developers to report issues and make feature requests for their development services, I'd urge you to make a feature request there. The best component to file this under would be the Google Admin SDK component, with the Feature Request template.
I was not able to use Reseller API (ran into some authorization issues) and Reports API (contained null values in all relevant attributes). The only way I was able to find how many licenses were remaining was through Enterprise License Manager API.
After getting assignments, I used sdkName to filter records based on the type of license.
Here is the complete code.
function getRemainingGoogleUserLicensesCount() {
const GOOGLE_USER_LICENSES_TOTAL = 100 // You can find this from Google Admin -> Billing -> Subscriptions
const productId = 'Google-Apps';
const customerId = 'C03az79cb'; // You can find this from this response, https://developers.google.com/admin-sdk/directory/v1/guides/manage-users#json-response
let assignments = [];
let usedLicenseCount = 0
let pageToken = null;
do {
const response = AdminLicenseManager.LicenseAssignments.listForProduct(productId, customerId, {
maxResults: 500,
pageToken: pageToken
});
assignments = assignments.concat(response.items);
pageToken = response.nextPageToken;
} while (pageToken);
for (const assignment of assignments) {
if (assignment["skuName"] == "Google Workspace Business Plus") {
usedLicenseCount += 1
}
}
return GOOGLE_USER_LICENSES_TOTAL - usedLicenseCount
}
During the creation of a new version for a model after the selection of a bucket and folder I got this error from the Cloud Console.
{
"error": {
"code": 400,
"message": "Field: version.deployment_uri Error: The model directory gs://ml-codelab/v1-output/ is expected to contain the 'export.meta' file. Please make sure it exists and Cloud ML service account cloud-ml-service#xxx.iam.gserviceaccount.com has read access to it",
"status": "FAILED_PRECONDITION",
"details": [
{
"#type": "type.googleapis.com/google.rpc.BadRequest",
"fieldViolations": [
{
"field": "version.deployment_uri",
"description": "The model directory gs://ml-codelab/v1-output/ is expected to contain the 'export.meta' file. Please make sure it exists and Cloud ML service account cloud-ml-service#xxxx.iam.gserviceaccount.com has read access to it"
}
]
}
]
}
}
You need to create a meta graph when you export your model. You can do this using a saver e.g.
saver = tf.train.Saver()
saver.save(sess, os.path.join(FLAGS.output_dir, "export"))
Typically you save the session and graph separately because your serving graph can be different from the training graph.
I want to integrate the WSO2 API Manager with a website so that the user doesn't need to login to the API Store.
From what I can see there is a stumbling block, in that the user needs to click on the 'Generate' button in the store in order to first generate the consumer key and secret.
Once this has been done then on it is possible to call the subscription API and generate token API as documented:
$ curl -b cookies http://localhost:9763/store/site/blocks/subscription/subscription-list/ajax/subscription-list.jag?action=getAllSubscriptions
{
"error" : false,
"subscriptions" : [
{
{
"sandRegenarateOption" : true,
"prodKey" : "2486e65cbac4e372fb319375744fb",
"subscriptions" : [
{
...
"prodConsumerSecret" : "Tx9i9WYu6_a3qqW08bF7jEG660",
"prodConsumerKey" : "VfS5r5u4rFhec2vVBlFosxRgcE",
"prodAuthorizedDomains" : "ALL"
...
}
],
...
"prodConsumerSecret" : "Tx9i9WYu6_a3qqW08bF7jEG660",
"prodConsumerKey" : "VfS5r5u4rFhec2vVBlFosxRgcE"
...
}
]
}
However until the 'Generate' button has been clicked in the browser, the values above are null
When I tried using the suggestion:
$ curl -X POST -b cookies http://localhost:9763/store/site/blocks/subscription/subscription-add/ajax/subscription-add.jag -d "action=generateApplicationKey&application=4&keytype=PRODUCTION&provider=admin&tier=Unlimited&version=0.1&callbackUrl=&authorizedDomains="
The server logs:
ERROR - APIStoreHostObject Error while obtaining the application access token for the application:4
org.wso2.carbon.apimgt.api.APIManagementException: Application should be approved before registering.
at org.wso2.carbon.apimgt.impl.APIConsumerImpl.requestApprovalForApplicationRegistration(APIConsumerImpl.java:1678)
at org.wso2.carbon.apimgt.impl.UserAwareAPIConsumer.requestApprovalForApplicationRegistration(UserAwareAPIConsumer.java:34)
at org.wso2.carbon.apimgt.hostobjects.APIStoreHostObject.jsFunction_getApplicationKey(APIStoreHostObject.java:649)
But according to the getApplications API it is approved already:
curl -b cookies http://localhost:9763/store/site/blocks/application/application-list/ajax/application-list.jag?action=getApplications
{
"applications" : [
...
{
"tier" : "Unlimited",
"status" : "APPROVED",
"callbackUrl" : "",
"name" : "app2",
"id" : 4,
"description" : ""
}
],
"error" : false
}
Any more thoughts ?
You can try the following REST invocation
curl -X POST -b cookies http://localhost:9763/store/site/blocks/subscription/subscription-add/ajax/subscription-add.jag -d "action=generateApplicationKey&application=DefaultApplication&keytype=PRODUCTION&provider=admin&tier=Unlimited&version=1.0.0&callbackUrl=&authorizedDomains="
I do not see any direct way to get that done. But you can make use of Store API exposed by wso2 API manger and retrieve all the subscription and from that you can filter the required ConsumerKey and ConsumerSecret.
I have tried this and worked pretty well to me :)
You need to promote before subscribing. You need to call "/site/blocks/life-cycles/ajax/life-cycles.jag" endpoint with "PUBLISHED" status and "updateStatus" action. Once it is in promoted state you can subscribe to it.