【问题标题】:Access permissions on AWS API GatewayAWS API Gateway 的访问权限
【发布时间】:2018-06-14 12:54:30
【问题描述】:

我正在构建一个应用程序,用户可以通过 Rest API 访问 DynamoDb 中的一些数据。

我的想法是:

  1. 用户访问 API 网关,通过 Cognito 用户池进行身份验证;
  2. API Gateway 调用 Lambda 函数(用 Python 编写);
  3. Lambda 函数访问 DynamoDB 并返回数据。

我希望能够根据用户限制对 DynamoDb 的访问级别。最初我以为 Lambda 函数可以从用户那里继承其权限,但这不起作用,因为它需要一个执行角色。

实现这一目标的最佳方法是什么?例如,我是否可以将用户信息传递给 Lambda 函数,该函数又可以在访问 DynamoDb 之前承担此角色?如果是这样,将不胜感激代码示例。

【问题讨论】:

    标签: amazon-web-services aws-api-gateway aws-cognito


    【解决方案1】:

    看看SpaceFinder - Serverless Auth Reference AppUse API Gateway Lambda Authorizers

    通过 Cognito,您可以使用 RBAC:

    Amazon Cognito 身份池为您的经过身份验证的用户分配一组 访问您的 AWS 资源的临时、有限的权限凭证。 每个用户的权限通过 IAM 角色控制 你创造。您可以定义规则来为每个用户选择角色 基于用户 ID 令牌中的声明。您可以定义默认角色 对于经过身份验证的用户。您还可以定义一个单独的 IAM 角色 未通过身份验证的来宾用户的有限权限。

    因此您可以为每个用户创建特定的角色,尽管使用组会更好

    您可以使用 Lambda 授权人创建自己的策略。 awslabs 中有一个例子。

    【讨论】:

    • 谢谢 - 我知道 Cognito 允许我为每个用户/组设置 IAM 角色。我不明白的是他们如何到达支持 API 的 Lambda,例如我创建了一个 lambda get_data,其执行角色对 DynamoDb 具有读/写权限。然后,只有 DynamoDb 只读权限的用户调用 API,而 API 又调用 get_dataget_data 函数如何知道此时它应该只有只读权限?
    • 在 Lambda 授权方场景中,决策是在授权方内部做出的。如果需要锁定 lambda,则需要使用 sdk 直接调用 lambda。请参阅此处进行更长时间的讨论:forum.serverless.com/t/… 使用 API Gateway 时,lambda 以配置的角色运行,并且必须自行检查或信任授权方。
    【解决方案2】:

    除了 blueCat 的回答之外,我还简单地尝试授予我的 Lambda 函数 sts:AssumeRole 权限,然后允许它承担通过 API 调用它的 Cognito 用户的角色。然后我可以使用它来获取一组新的凭据并使用 Cognito 用户的权限执行一些活动。 lambda里面的代码大致是:

    def lambda_handler(event, context):
        sts_client = boto3.client('sts')
        role = event['requestContext']['authorizer']['claims']['cognito:roles']
        cognito_role = sts_client.assume_role(
            RoleArn=role,
            RoleSessionName='lambda-session',
            DurationSeconds=3600
        )
        credentials = cognito_role['Credentials']
        sess = boto3.session.Session(
            aws_access_key_id=credentials['AccessKeyId'], 
            aws_secret_access_key=credentials['SecretAccessKey'],
            aws_session_token=credentials['SessionToken']
        )
        # Do something as the assumed user, e.g. access S3
        s3_client = sess.client('s3')
        # Do stuff here...
    

    虽然这可行,但我发现承担角色和获取 S3 客户端大约需要 0.5 秒的开销,并且我无法在函数调用之间重用此会话,因为它是特定于用户的。因此,这种方法并不真正适合我的应用程序。

    我决定改为授予我的 Lambda 对相关 DynamoDb 表的完全访问权限,并使用 Cognito 用户组和 Lambda 授权者来限制单个用户能够调用的 API 部分。

    【讨论】:

      【解决方案3】:

      我也处理过这个问题,但我用Node.js 实现了我的解决方案,我认为虽然你的问题是关于 Python 实现的,但也许有人会偶然发现这个问题,在 JS 中寻找答案,我认为这可以帮助下一个出现的人。

      在用户使用自定义属性针对您的 Cognito 用户池拥有 Authenticated 凭据后,您似乎正在尝试提出有效的 Authorization 策略。

      我创建了一个库,用于导出一些函数,这些函数允许我为 authenticated 用户捕获 UserPoolIdUsername,以便我可以捕获 custom:<attribute> 我需要在我的 lambda 中,以便我已实现的条件可以将 API 用于我需要为我的应用身份验证的每个用户提供授权的其余 AWS 服务。

      这是我的图书馆:

      import AWS from "aws-sdk";
      // ensure correct AWS region is set
      AWS.config.update({
          region: "us-east-2"
      });
      
      // function will parse the user pool id from a string
      export function parseUserPoolId(str) {
          let regex = /[^[/]+(?=,)/g;
          let match = regex.exec(str)[0].toString();
      
          console.log("Here is the user pool id: ", match);
      
          return match.toString();
      }
      
      // function will parse the username from a string
      export function parseUserName(str) {
          let regex = /[a-z,A-Z,0-9,-]+(?![^:]*:)/g;
          let match = regex.exec(str)[0].toString();
      
          console.log("Here is the username: ", match);
      
          return match.toString();
      }
      
      // function retries UserAttributes array from cognito
      export function getCustomUserAttributes(upid, un) {
          // instantiate the cognito IdP
          const cognito = new AWS.CognitoIdentityServiceProvider({
              apiVersion: "2016-04-18"
          });
      
          const params = {
              UserPoolId: upid,
              Username: un
          };
      
          console.log("UserPoolId....: ", params.UserPoolId);
          console.log("Username....: ", params.Username);
      
          try {
              const getUser = cognito.adminGetUser(params).promise();
              console.log("GET USER....: ", getUser);
              // return all of the attributes from cognito
              return getUser;
          } catch (err) {
              console.log("ERROR in getCustomUserAttributes....: ", err.message);
              return err;
          }
      }
      

      实现了这个库后,它现在可以被您需要为其创建授权策略的任何 lambda 使用。

      在你的 lambda 中,你需要导入上面的库(我省略了下面的导入语句,你需要添加它们以便访问导出的函数),你可以这样实现它们的使用::

      export async function main(event, context) {
        const upId = parseUserPoolId(
          event.requestContext.identity.cognitoAuthenticationProvider
        );
        // Step 2 --> Get the UserName from the requestContext
        const usrnm = parseUserName(
          event.requestContext.identity.cognitoAuthenticationProvider
        );
        // Request body is passed to a json encoded string in
        // the 'event.body'
        const data = JSON.parse(event.body);
      
        try {
          // TODO: Make separate lambda for AUTHORIZATION
          let res = await getCustomUserAttributes(upId, usrnm);
      
          console.log("THIS IS THE custom:primaryAccountId: ", res.UserAttributes[4].Value);
          console.log("THIS IS THE custom:ROLE: ", res.UserAttributes[3].Value);
          console.log("THIS IS THE custom:userName: ", res.UserAttributes[1].Value);
      
          const primaryAccountId = res.UserAttributes[4].Value;
      
        } catch (err) {
          // eslint-disable-next-line
          console.log("This call failed to getattributes");
          return failure({
            status: false
          });
        }
      }
      
      
      

      来自 Cognito 的响应将提供一个包含您需要的自定义属性的数组。 Console.log 使用 console.log("THIS IS THE Cognito response: ", res.UserAttributes); 记录来自 Cognito 的响应,并检查 CloudWatch 日志中所需属性的索引号,并调整所需的索引:

      res.UserAttributes[n]

      现在您有一个authorization 机制,您可以在 lambda 中使用该机制以允许用户发布到 DynamoDB,或使用您的应用程序中的任何其他 AWS 服务,并为每个经过身份验证的用户提供正确的授权。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-08-19
        • 2020-05-11
        • 2020-02-20
        • 2019-04-29
        • 1970-01-01
        • 2020-05-18
        • 2017-05-01
        相关资源
        最近更新 更多