AWS Cognito ListUsers InvalidParameterException using AttributesToGet on custom attributes - node.js

Using the following AWS Lambda-based app client, I'm trying to list all users from my Cognito user pool.
let AWS = require('aws-sdk')
const COGNITO_CLIENT = new AWS.CognitoIdentityServiceProvider()
COGNITO_CLIENT.listUsers({
UserPoolId: 'MyUserPoolId',
AttributesToGet: ['default_attribute', 'custom:my_attribute']
}, callback)
Everything works fine when querying for all attributes by default (AttributesToGet: [] // or excluding this field altogether). However, when targeting custom attributes, the InvalidParameterException is raised. This is using the Amazon SDK for Node.js.
Targeting default attributes are allowed though:
AttributesToGet: ['email', 'name', /* other non-custom */]

Please remove the "AttributesToGet" and try.

Your code is correct. However, I am on the Cognito team and we don't support search on custom attributes at this point.

Related

How to get past errors using putParameter with aws-sdk for nodejs in Lambda?

I'm trying to set a parameter using putParameter in the AWS SDK for JavaScript in Node.js. In particular, I'd like to take advantage of the "Advanced" Tier, with an Expiration policy and Tags if possible. When I execute my code, I keep getting errors like:
There were 2 validation errors:
* UnexpectedParameter: Unexpected key 'Policies' found in params
* UnexpectedParameter: Unexpected key 'Tier' found in params
I suspected the issue was around the aws-sdk version I was using, so I've tried running the code locally using SAM local, and from Lambda functions using the nodejs8.10 and nodejs10.x environments. The errors do not go away.
const AWS = require('aws-sdk');
AWS.config.update({region: 'us-east-1'});
const ssm = new AWS.SSM({apiVersion: '2014-11-06'});
exports.lambdaHandler = async () => {
const tokenExpiration = new Date();
tokenExpiration.setSeconds(tokenExpiration.getSeconds() + 60);
await ssm.putParameter({
Name: 'SECRET_TOKEN',
Type: 'SecureString',
Value: '12345',
Policies: JSON.stringify([
{
"Type":"Expiration",
"Version":"1.0",
"Attributes":{
"Timestamp": tokenExpiration.toISOString()
}
}
]),
Overwrite: true,
Tier: 'Advanced'
}).promise();
};
I would expect this code to work and set a parameter with the expiration. However, it appears that the sdk doesn't recognize the "Policies" and "Tier" parameters, which are available according to the documentation. I don't know if it's an issue of waiting for the newest AWS SDK for JavaScript, but the runtimes page suggest that nodejs10.x is running AWS SDK for JavaScript 2.437.0.
It might be helpful to know that I can get the code running correctly without the parameters in question (ie, just the "Name", "Type", and "Value" parameters).
Unfortunately both Tier and Policies weren't added until v2.442.0 (see diff)
This means that to use these features you'll have to deploy with the version of the aws-sdk you're developing against.
It should be noted that either developing/testing against the built-in version, or deploying with the aws-sdk you do use, is often cited as good practice. If you're deploying your version you can use explicit client imports (e.g. const SSM = require('aws-sdk/clients/ssm') to keep the deployment size down. This is even more effective if you develop against the preview AWS-SDK Version 3.

Independent NPM library that validates request based on swagger file

We are building APIs using Swagger, AWS API gateway and Lambda functions with NodeJS. The API gateway will do the request validation, however as per the design, the lambda functions need to re-validate the request object as an API Gateway Proxy Request Event. This makes sense as in theory we can reuse the lambda functions by invoking them via other event source (e.g. SNS).
Therefore we need an NodeJS tool which can validate the request (not only body but also params, etc) based on the swagger spec - exactly what the swagger-tools and a few other tools (e.g. swagger-request-validator) are doing, but not as a middleware.
I did some search but could not find one, also looked into swagger-tools source code, reckon its validation component was written in the way that cannot be easily used separately.
Any suggestion is welcome. Thanks in advance.
You can use swagger-model-validator.
var Validator = require('swagger-model-validator');
var swaggerFile = require("./swagger.json");
const validator = new Validator(swaggerFile);
console.log(validator.validate({
name: 'meg'
}, swaggerFile.definitions.Pet, swaggerFile.definitions, true).GetErrorMessages())
This outputs:
[ 'photoUrls is a required field' ]
validator.validate returns an object, so you can also check if the returned object contains anything under the errors attribute. It should be as simple as
if (validator.validate({
name: 'meg'
}, swaggerFile.definitions.Pet, swaggerFile.definitions, true).errors) {
// do something with error
}
I have used Swagger's sample JSON for this answer.

SageMaker NodeJS's SDK is not locking the API Version

I am running some code in AWS Lambda that dynamically creates SageMaker models.
I am locking Sagemaker's API version like so:
const sagemaker = new AWS.SageMaker({apiVersion: '2017-07-24'});
And here's the code to create the model:
await sagemaker.createModel({
ExecutionRoleArn: 'xxxxxx',
ModelName: sageMakerConfigId,
Containers: [{
Image: ecrUrl
}]
}).promise()
This code runs just fine locally with aws-sdk on 2.418.0.
However, when this code is deployed to Lambda, it doesn't work due to some validation errors upon creating the model:
MissingRequiredParameter: Missing required key 'PrimaryContainer' in params
UnexpectedParameter: Unexpected key 'Containers' found in params
Is anyone aware of existing bugs in the aws-sdk for NodeJS using the SDK provided by AWS in the Lambda context? I believe the SDK available inside AWS Lambda is more up-to-date than 2.418.0 but apparently there are compatibility issues.
As you've noticed the 'embedded' lambda version of the aws-sdk lags behind. It's actually on 2.290.0 (you can see the full details on the environment here: https://docs.aws.amazon.com/lambda/latest/dg/current-supported-versions.html)
You can see here: https://github.com/aws/aws-sdk-js/blame/master/clients/sagemaker.d.ts that it is not until 2.366.0 that the params for this method included Containers and did not require PrimaryContainer.
As you've noted, the workaround is to deploy your lambda with the aws-sdk version that you're using. This is sometimes noted as a best practice, as it pins the aws-sdk on the functionality you've built and tested against.

Trying to insert data into BigQuery fails from container engine pod

I have a simple node.js application that tries to insert some data into BigQuery. It uses the provided gcloud node.js library.
The BigQuery client is created like this, according to the documentation:
google.auth.getApplicationDefault(function(err, authClient) {
if (err) {
return cb(err);
}
let bq = BigQuery({
auth: authClient,
projectId: "my-project"
});
let dataset = bq.dataset("my-dataset");
let table = dataset.table("my-table");
});
With that I try to insert data into BiqQuery.
table.insert(someRows).then(...)
This fails, because the BigQuery client returns a 403 telling me that the authentication is missing the required scopes. The documentation tells me to use the following snippet:
if (authClient.createScopedRequired &&
authClient.createScopedRequired()) {
authClient = authClient.createScoped([
"https://www.googleapis.com/auth/bigquery",
"https://www.googleapis.com/auth/bigquery.insertdata",
"https://www.googleapis.com/auth/cloud-platform"
]);
}
This didn't work either, because the if statement never executes. I skipped the if and set the scopes every time, but the error remains.
What am I missing here? Why are the scopes always wrong regardless of the authClient configuration? Has anybody found a way to get this or a similar gcloud client library (like Datastore) working with the described authentication scheme on a Container Engine pod?
The only working solution I found so far is to create a json keyfile and provide that to the BigQuery client, but I'd rather create the credentials on the fly then having them next to the code.
Side note: The node service works flawless without providing the auth option to BigQuery, when running on a Compute Engine VM, because there the authentication is negotiated automatically by Google.
baking JSON-Keyfiles into the images(containers) is bad idea (security wise [as you said]).
You should be able to add these kind of scopes to the Kubernetes Cluster during its creation (cannot be adjusted afterwards).
Take a look at this doc "--scopes"

ACL in Node js based on relationships

I'm trying to implement some kind of permission framework in Node js, using sequelize as an ORM (with Postgres). After hours of research, the closest thing I can find to do this with existing npm modules is using acl with acl sequelize to support my stack.
The problem is that it looks like the acl module assigns a role, where that role would get a set of permissions to all instances of a specific resource. However, I need to do permissioning for instances based on existing relationships of that user.
As an example, consider a permissioning system for a simple forum. It gives these permissions for each role:
// allow guests to view posts
acl.allow("guest", "post", "view");
// allow registered users to view and create posts
acl.allow("registered users", "post", ["view", "create"]);
// allow administrators to perform any action on posts
acl.allow("administrator", "post", "*");
Suppose that I want to also add the ability for registered users to also edit their own posts, and the user has a relationship to all the posts they've created.
Is there any way for this module to do this, or any other module that can support this kind of behavior on the database / ORM level?
If not, and I have to implement a custom one, what would the best approach to creating something like this.
There is relatively new library CASL. I'm the author of this library. And it's possible to implement your usecase quite easily:
const { AbilityBuilder } = require('casl')
const ability = AbilityBuilder.define((can, cannot) => {
can('read', 'all')
can(['update', 'delete'], 'Article', { author_id: loggedInUser.id })
})
The code above basically says:
- anyone can read everything
- anyone can update and delete articles where author_id equals logged in user id
Later you can do:
ability.can('delete', 'Post')
//or
ability.can('update', post)
// where post variable is an instance of your Post model
Also there is an article which explains how to integrate CASL with MongoDB and Express exactly for your usecase.

Resources