ccasanovas/cake-cognito

CognitoSDK插件,用于CakePHP

安装: 1

依赖: 0

建议者: 0

安全: 0

星标: 0

关注者: 1

分支: 0

开放问题: 0

类型:composer-plugin

1.0.11 2023-02-04 23:43 UTC

This package is auto-updated.

Last update: 2024-09-06 23:51:54 UTC


README

此插件允许从应用程序面板管理AWS Cognito用户,并确保与本地副本保持同步。

此插件假定与AWS Cognito之间存在单向通信通道,其中同步字段的更改首先在应用程序中发生,然后由插件在AWS Cognito中传播。

变更日志

日期:2018-10-11

  • 重写了全部文档以反映最近的更改

文档

功能

实现

  • 用户注册。向用户发送带有临时密码的邀请电子邮件
  • 用户注销,确保也从Cognito中移除
  • 通过“活动”复选框禁用/启用用户(阻止登录)
  • 密码重置
  • 更改电子邮件,可选择是否发送验证码
  • 重新发送邀请电子邮件(创建账户)
  • 用户资料表明用户是否与Cognito正确同步
  • 将用户头像上传到AWS S3
  • JSON Web Tokens (AwsCognitoJwtAuthenticate)身份验证器用于验证Cognito用户
  • 非管理员用户的API端点
  • 以CSV格式批量导入用户,并进行预先验证

待办事项

  • 配置使用电话、电子邮件或两者进行用户注册/访问(目前仅支持电子邮件)
  • 用户导入在失败时无法撤销编辑。仅用于创建新用户。
  • 允许管理员更改用户头像

注意事项

  • 如果创建的用户被创建为“禁用”(active = 0),则插件将在创建后尝试在Cognito中禁用它(因为Cognito中不能创建禁用用户)。如果失败,插件将优先考虑一致性,用户将被创建为启用。

安装

要使用composer获取插件,需要在composer.json中添加以下内容

  1. "require"对象中添加插件:"ccasanovas/cake-cognito": "dev-master"
  2. "repositories"数组中添加对象:{"type": "vcs", "url": "git@bitbucket.org:ccasanovas/cake-cognito.git"}
  3. 运行composer update

注意:确保在存储库中具有正确的访问/部署权限。

一旦在存储库中安装了插件

  1. config/bootstrap.php中添加以下行以加载插件:Plugin::load('Ccasanovas/CognitoSDK', ['bootstrap' => true, 'routes' => true]);
  2. 确保存在api_users表。插件附带一个迁移以供使用:bin/cake migrations migrate -p Ccasanovas/CognitoSDK
  3. 在Cognito中创建一个用户池。注意
    • 用户池必须仅包含访问所需的数据。额外字段应仅存在于本地表中。
    • 确保Cognito创建用户所需字段的验证与本地表验证相同或更宽松,否则在创建用户时可能会发生错误,这些错误在用户看来不会显示。
    • 不允许用户通过Cognito更改其电子邮件或电话号码,这可能导致数据不同步。除了更改密码外,所有用户更改都应通过插件提供的API进行。
  4. 在加载插件之前,将以下行添加到 config/bootstrap.php 中:Configure::write('CognitoSDK.config', ['awscognito']);
  5. 将文件 vendor/ccasanovas/cake-cognito/config/awscognito.php 复制到 app/config/awscognito.php
  6. 修改文件以配置插件。(请参阅“配置变量”部分)
  7. 默认路由如下(要更改此设置,请参阅“扩展插件”部分。要禁用默认路由,请在 Plugin::load 中使用 'routes' => false
    • 插件的用户控制器应可通过 /aws-cognito/api-users 访问
    • API端点可通过 /aws-cognito/api/api-users/:action 访问

结构

Ccasanovas\CognitoSDK\Model\Entity\ApiUser

属性

  • aws_cognito_id (36字符) (隐藏,不可变)
  • aws_cognito_username (255字符) (隐藏,不可变)
  • email (255字符)
  • active (1位小整数)
  • role (50字符)
  • first_name (50字符)
  • last_name (50字符)
  • created (日期时间)
  • modified (日期时间)

虚拟属性

  • full_name ("${first_name} ${last_name}")

Ccasanovas\CognitoSDK\Model\Table\ApiUsersTable

  • 受保护 $searchQueryFields
    • 用于表搜索配置的字段数组。
    • 抽象为受保护的属性,以简化其扩展。
  • 公共 initialize()
    • 配置 CreatorsUsers 表的关联(created_by)以及 ModifiersUsers 表的关联(modified_by)。要更改使用的表,必须扩展表,请参阅“扩展插件”部分。
    • 添加 Timestamp 行为以填充字段 created_atmodified_at
    • 添加 Muffin/Footprint.Footprint 行为以填充字段 created_bymodified_by
    • 添加 Ccasanovas/CognitoSDK.CognitoSDK 行为以启用Cognito功能
    • 添加 Ccasanovas/CognitoSDK.ImportApiUsers 行为以添加批量导入功能
    • 添加 Ccasanovas/S3UploadSDK.S3UploadSDK 行为以将用户头像上传到AWS S3
    • 添加 Search.Search 行为以启用搜索
  • 公共 validationDefault(Validator $validator)
    • 对基本字段(id,first_name,last_name)进行基本验证
    • 确保 role 字段存在且属于配置的列表(要更改此设置,必须扩展验证,请参阅“扩展插件”部分)。
  • 公共 getRoles()
    • 返回配置的角色的数组。
    • 如果配置不存在或无效,则抛出异常。

Ccasanovas\CognitoSDK\Model\Traits\AwsCognitoSaveTrait

此特质由 ApiUsersTable 使用。其功能是

  • 重写 saveMany 方法以捕获在尝试同时保存多个用户时发生的任何 Cognito 异常或保存错误。
  • 在这种情况下,将负责回滚 Cognito 中造成的任何更改,必要时删除新用户。
  • 未来还应负责回滚编辑。

Ccasanovas\CognitoSDK\Model\Behavior\AwsCognitoBehavior

此行为负责启用所有与 Cognito 相关的功能。其方法如下

  • 公共 validationResendInvitationEmail(Validator $validator)
    • 包含用于重新发送邀请电子邮件动作的验证器
  • 公共 validationChangeEmail(Validator $validator)
    • 包含用于更改电子邮件动作的验证器
  • 公共 buildValidator(Event $event, Validator $validator, $name)
    • 验证器回调
    • 确保 Cognito 字段是必需的,并且相应地进行验证
  • 公共 buildRules(Event $event, RulesChecker $rules)
    • 添加应用程序规则
    • 确保Cognito用户名唯一
    • 确保电子邮件唯一
    • 阻止编辑Cognito用户名
    • 阻止编辑Cognito ID
    • 阻止通过传统方式编辑电子邮件(提供changeEmail和resendInvitationEmail方法以更改电子邮件的选项)
  • public beforeSave(Event $event, EntityInterface $entity, ArrayObject $options)
    • 在保存用户之前,此回调执行以下操作
      • 如果用户是新的,调用createCognitoUser
      • 如果用户正在编辑且active字段已更改,则调用disableCognitoUserenableCognitoUser,具体取决于情况
  • public beforeDelete(Event $event, EntityInterface $entity, ArrayObject $options)
    • 在从数据库中删除用户之前,此回调调用deleteCognitoUser以在Cognito中删除它。
  • public changeEmail(EntityInterface $entity, $new_email, bool $require_verification = true)
    • 此方法封装了用户电子邮件更改,以便可以从Controller方便地调用它。
    • 在Cognito中更改电子邮件,并在成功的情况下将更改保存在数据库中。
    • 通过参数$require_verification允许选择电子邮件是否需要验证。
    • 如果需要验证,Cognito将向用户发送带有验证码的电子邮件。客户端的开发者可以使用此验证电子邮件。
  • public resendInvitationEmail(EntityInterface $entity, $new_email)
    • 如果账户已过期(刷新账户)或电子邮件地址不正确(允许更改),则重新发送邀请电子邮件。
  • public getWithCognitoData($id, $options = [])
    • 在数据库中查找用户并添加Cognito信息
    • 调用Cognito添加字段,因此性能不是很高。
    • 在Controller的客户详细信息中使用。
    • 添加以下字段
      • bool aws_cognito_synced:指示用户是否已将所有相关字段正确同步到Cognito。通常永远不会为假。
      • array aws_cognito_attributes:Cognito额外属性的列表
      • array aws_cognito_status:包含显示Cognito状态的字段,解析后更具可读性
        • aws_cognito_status['code']:状态代码
        • aws_cognito_status['title']:状态标题
        • aws_cognito_status['description']:状态描述
    • 如果未在Cognito中找到用户,则不会抛出异常,字段为空,并且aws_cognito_synced将为假。
  • public resetCognitoPassword(EntityInterface $entity)
    • 清除密码。
    • 将启动密码更改流程,以便下次用户登录时进行更改。
    • 如果用户从未登录过,或者电子邮件/电话未验证以发送验证消息,则无法清除密码。
  • public deleteCognitoUser(EntityInterface $entity)
    • 从Cognito中删除用户。
    • 在从本地表中删除用户时自动使用。
    • 此方法为公共的,因为它对批处理和事务过程很有用。
  • protected createCognitoClient()
    • 用于初始化与Cognito连接的内部方法。
  • protected processCognitoUser(Result $cognito_user)
    • 处理Cognito SDK返回的用户数据,并将其转换为更适合CakePHP的格式。
  • protected titleForUserStatus($status)
    • 返回Cognito用户的状态标题
  • protected descriptionForUserStatus($status)
    • 返回Cognito用户的状态描述
  • protected createCognitoUser(EntityInterface $entity, $message_action = null)
    • 用于在Cognito中创建新用户或重新发送邀请电子邮件。
  • 受保护 awsExceptionToEntityErrors(AwsException $exception, EntityInterface $entity)
    • 解析Cognito的某些异常并将它们转换为实体的验证错误
    • 涵盖电子邮件和无效或重复的用户等案例
    • 由其他方法使用,以使过程更加CakePHP-friendly
  • 受保护 disableCognitoUser(EntityInterface $entity)
    • 在Cognito中禁用用户,阻止其登录
  • 受保护 enableCognitoUser(EntityInterface $entity)
    • 在Cognito中启用用户,恢复其登录

Ccasanovas\CognitoSDK\Model\Behavior\ImportApiUsersBehavior

此Behavior负责添加用户导入功能。目前,这包括以CSV格式批量导入用户的能力。其方法包括:

  • public validateMany(array $rows, $max_errors = false, array $options = []): array
    • $rows是一个用户数组,可以包含CakePHP的实体或格式化为CakePHP的用户数据数组
    • 此方法将数组$rows中的所有用户转换为CakePHP的实体
    • 使用用户名来查找现有实体,并使用输入的更改进行修补。其余的将被创建。
    • 使用实体验证和应用程序规则对所有实体进行验证
    • 此外,还会在它们之间进行验证以查找重复项
    • 返回一个应用了所有验证的实体数组。可以通过调用$entity->getErrors()来查看错误
  • public csvDataToAssociativeArray(string $csv_data, array $fields = []): array
    • 将输入的CSV数据格式转换为 CakePHP 格式的用户数组。
    • 期望字段按照特定顺序排列
    • 默认期望基本字段(aws_cognito_usernameemailfirst_namelast_name
    • 要更改字段顺序或数量,请使用参数 $fields。

Ccasanovas\CognitoSDK\Controller\ApiUsersController

使用以下 Traits

  • BaseCrudTrait
  • ImportApiUsersTrait
  • AwsCognitoTrait

Ccasanovas\CognitoSDK\Controller\Traits\BaseCrudTrait

为管理员用户提供以下基本操作

  • /aws-cognito/api-users/index (GET)
    • 用户列表
  • /aws-cognito/api-users/view/:id (GET)
    • 用户详情
    • 包含Cognito的数据
  • /aws-cognito/api-users/add (GET, POST)
    • 添加用户
  • /aws-cognito/api-users/edit/:id (GET, POST)
    • 编辑用户的基本字段(first_name, last_name等)
  • /aws-cognito/api-users/change-email/:id (GET, POST)
    • 更改用户电子邮件
    • 允许决定电子邮件是否需要验证(在这种情况下,将发送验证电子邮件)
  • /aws-cognito/api-users/delete/:id (POST, DELETE)
    • 删除用户

Ccasanovas\CognitoSDK\Controller\Traits\ImportApiUsersTrait

提供用户导入操作

  • /aws-cognito/api-users/import (GET, POST)
    • 允许通过CSV格式的文本字段批量导入用户

Ccasanovas\CognitoSDK\Controller\Traits\AwsCognitoTrait

提供Cognito专用功能的操作

  • /aws-cognito/api-users/activate/:id (POST)
    • 激活用户(允许登录)
  • /aws-cognito/api-users/deactivate/:id (POST)
    • 禁用用户(阻止登录)
  • /aws-cognito/api-users/reset-password/:id (POST)
    • 清除用户密码
    • 将启动密码更改流程,以便下次用户登录时进行更改。
    • 如果用户从未登录过,或者电子邮件/电话未验证以发送验证消息,则无法清除密码。
  • /aws-cognito/api-users/resend-invitation-email/:id (GET, POST)
    • 重新发送邀请电子邮件
    • 刷新账户过期时间
    • 允许更改电子邮件

Ccasanovas\CognitoSDK\Controller\Api\ApiUsersController

使用 BaseApiEndpointsTrait

Ccasanovas\CognitoSDK\Controller\Traits\BaseApiEndpointsTrait

为客户端开发者提供以下端点

  • /aws-cognito/api/api-users/profile (GET)
    • 用户详情
  • /aws-cognito/api/api-users/profile (POST)
    • 允许编辑用户配置文件(仅限基本字段,如first_name和last_name)
  • /aws-cognito/api/api-users/change-email (POST)
    • 允许更改用户电子邮件
    • 将发送带有验证码的电子邮件到新地址。开发者需要使用它来与 cognito 通信并验证电子邮件。
  • /aws-cognito/api/api-users/upload-avatar (POST)
    • 允许更改用户头像
    • 期望在 HTTP 请求体中发送原始图像二进制数据
    • 将头像上传到 S3

配置变量

配置变量与应用程序的配置数组中的其余配置一起保存(默认为 config/app.php)。

可用的配置有

'CognitoSDK' => [
    'AccessKeys' => [
        'id' => 'NSWPXE30F49XAOF',
        'secret' => 'QIQNxRO2425bb040e4adc8cc02fae05063c3c'
    ],
    'UserPool' => [
        'id' => 'us-east-2_rjaob1HaR',
    ],
    'IdentityProviderClient' => [
        'settings' => [], //https://docs.aws.amazon.com/sdkforruby/api/Aws/CognitoIdentityProvider/Client.html#initialize-instance_method
    ],
],
'ApiUsers' => [
    /* available user roles */
    'roles' => [
        'user' => __d('Ccasanovas/CognitoSDK', 'API User'),
    ],
    /* the max amount of errors alloweds before the validation process of the imported data is halted */
    'import_max_errors' => 10,

    /* the limit of accepted rows in the importing CSV data */
    'import_max_rows' => 500,
]

测试

目前该插件大约有 90% 的代码行覆盖率,以及 70% 的方法和函数覆盖率。

要运行测试,必须单独下载仓库(以便 composer 注册开发模式)。运行 composer install 以安装依赖项,然后使用 vendor/bin/phpunit 运行测试。

扩展插件

更改路由

要更改插件的默认路由(/aws-cognito/api-users),可以在 config/routes.php 中添加以下内容,必须首先在 bootstrap.php 中通过 Plugin::load 载入插件时将 'routes' => false 设置为 true。

然后,您可以在 config/routes.php 中的基础作用域(/)内使用以下内容。

  • 将路由 /aws-cognito/api-users 替换为 /api-users
    $routes->connect('/api-users/*', [
        'plugin' => 'Ccasanovas/CognitoSDK', 'controller' => 'ApiUsers'
    ]);
    $routes->connect('/api-users/:action', [
        'plugin' => 'Ccasanovas/CognitoSDK', 'controller' => 'ApiUsers', 'action' => ':action'
    ]);
    $routes->connect('/api-users/:action/:id', [
        'plugin' => 'Ccasanovas/CognitoSDK', 'controller' => 'ApiUsers', 'action' => ':action'
    ],  ['id' => '\d+', 'pass' => ['id']]);
  • 对于 REST API,可以按照以下方式替换
    Router::prefix('api', function ($routes) {
        $routes->extensions(['json']);
    
         /* Api Users */
         $routes->connect('/me', [
            'plugin'     => 'Ccasanovas/CognitoSDK',
            'prefix'     => 'Api',
            'controller' => 'ApiUsers',
            'action'     => 'profile',
            '_method'    => 'GET'
        ]);
    
        $routes->connect('/me', [
            'plugin'     => 'Ccasanovas/CognitoSDK',
            'prefix'     => 'Api',
            'controller' => 'ApiUsers',
            'action'     => 'editProfile',
            '_method'    => 'PATCH'
        ]);
    
        $routes->connect('/me/avatar', [
            'plugin'     => 'Ccasanovas/CognitoSDK',
            'prefix'     => 'Api',
            'controller' => 'ApiUsers',
            'action'     => 'uploadAvatar',
            '_method'    => 'PUT'
        ]);
    
        $routes->connect('/me/email', [
            'plugin'     => 'Ccasanovas/CognitoSDK',
            'prefix'     => 'Api',
            'controller' => 'ApiUsers',
            'action'     => 'changeEmail',
            '_method'    => 'PUT'
        ]);
    
    });

覆盖视图

为了在不扩展控制器的情况下覆盖模板,需要将新的模板放在以下位置

src/Template/Plugin/Ccasanovas/CognitoSDK/ApiUsers/*

例如,要替换 index 模板

src/Template/Plugin/Ccasanovas/CognitoSDK/ApiUsers/index.ctp

扩展控制器/模型

如果需要,可以通过以下方式扩展控制器和模型(例如,向 ApiUsers 模型添加新关联)

首先,扩展 ApiUsersTable

//archivo: src/Model/Table/ApiUsersTable.php
namespace App\Model\Table;

use Ccasanovas\CognitoSDK\Model\Entity\ApiUser;
use Ccasanovas\CognitoSDK\Model\Table\ApiUsersTable as AwsApiUsersTable;

class ApiUsersTable extends AwsApiUsersTable
{
    //esto solo es necesario si no se va a extender la Entidad
    protected $_entityClass = 'Ccasanovas\CognitoSDK\Model\Entity\ApiUser';

    public function initialize(array $config)
    {
        parent::initialize($config);

        //cambiando la tabla de usuarioa administradores
        $this->association('Creators')->className('AppUsers');
        $this->association('Modifiers')->className('AppUsers');

        //agregar nuevas asociaciones aca
         $this->belongsToMany('FunctionalUnits', [
            'through' => 'ApiUsersFunctionalUnits',
            'saveStrategy' =>'append',
        ]);

        //y podemos facilmente reconfigurar el search cambiando estos valores
        $this->searchQueryFields = [
            'ApiUsers.aws_cognito_username',
            'ApiUsers.email',
            'ApiUsers.phone',
            'ApiUsers.first_name',
            'ApiUsers.last_name',
        ];
    }

    //extendiendo la validación por defecto
    public function validationDefault(Validator $validator)
    {
        $validator = parent::validationDefault($validator);

        //se puede remover la validacion de un campo si no se usa
        $validator->remove('role');

        //y agregar campos nuevos
        $validator
            ->scalar('phone')
            ->allowEmpty('phone');

        return $validator;
    }


}

然后扩展 ApiUsersController

//archivo: src/Controller/ApiUsersController.php
namespace App\Controller;

use Ccasanovas\CognitoSDK\Controller\ApiUsersController as AwsApiUsersController;
use App\Model\Table\ApiUsersTable;

class ApiUsersController extends AwsApiUsersController
{
    //aca se pueden agregar nuevas acciones o reemplazar las existentes

    //por ejemplo el index:
    public function index()
    {
        $this->paginate['contain'] = ['PointsOfSale'];

        $this->set('api_users', $this->paginate('ApiUsers'));
        $this->set('_serialize', ['api_users']);
    }
}

可选地可以扩展实体 ApiUser

//archivo: src/Model/Entity/ApiUser.php
namespace App\Model\Entity;

use Ccasanovas\CognitoSDK\Model\Entity\ApiUser as AwsApiUser;

class ApiUser extends AwsApiUser
{
    protected $_accessible = [
        '*' => true,
        'id' => false,
        'role' => false,

        //cognito fields:
        'aws_cognito_username' => false,
        'aws_cognito_id' => false,
        'email' => false,

        //dejando lo demás como viene, podemos agregar nuevos campos
        'phone' => false
    ];

    //podemos agregar nuevas virtual properties aca
}

现在,为了保留插件的模板,只需添加新的(或要替换的)模板,可以修改路由,以便新的操作使用此控制器,而其他操作则指向插件内的控制器

//el index lleva al nuevo controller
$routes->connect('/api-users', ['controller' => 'ApiUsers', 'action' => 'index']);
$routes->connect('/api-users/index', ['controller' => 'ApiUsers', 'action' => 'index']);

//las demas rutas llevan al controller del plugin
$routes->connect('/api-users/*', [
    'plugin' => 'Ccasanovas/CognitoSDK', 'controller' => 'ApiUsers'
]);
$routes->connect('/api-users/:action', [
    'plugin' => 'Ccasanovas/CognitoSDK', 'controller' => 'ApiUsers', 'action' => ':action'
]);
$routes->connect('/api-users/:action/:id', [
    'plugin' => 'Ccasanovas/CognitoSDK', 'controller' => 'ApiUsers', 'action' => ':action'
],  ['id' => '\d+', 'pass' => ['id']]);