diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 35d51f04..964b63a2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Create Release id: create_release uses: actions/create-release@v1 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e2edafc0..ab13a512 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -20,7 +20,7 @@ jobs: swoole: [ 'swoole' ] steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Setup PHP uses: shivammathur/setup-php@v2 with: diff --git a/composer.json b/composer.json index b292af6e..2923d686 100644 --- a/composer.json +++ b/composer.json @@ -92,9 +92,6 @@ "mineadmin/app-store": "*", "mineadmin/crontab": "*", "mineadmin/http-server": "*", - "mineadmin/security-access": "*", - "mineadmin/security-bundle": "*", - "mineadmin/security-http": "*", "mineadmin/support": "*" }, "minimum-stability": "dev", @@ -110,9 +107,6 @@ "Mine\\Helper\\": "src/mine-helpers/src/", "Mine\\HttpServer\\": "src/HttpServer/src/", "Mine\\Office\\": "src/office/src/", - "Mine\\SecurityBundle\\": "src/SecurityBundle/src/", - "Mine\\Security\\Access\\": "src/SecurityAccess/src/", - "Mine\\Security\\Http\\": "src/SecurityHttp/src/", "Mine\\Support\\": "src/Support/src/", "Mine\\Translatable\\": "src/translatable/src/", "Xmo\\JWTAuth\\": "src/jwt-auth/src/" @@ -126,9 +120,6 @@ "psr-4": { "Mine\\Crontab\\Tests\\": "src/Crontab/tests/", "Mine\\HttpServer\\Tests\\": "src/HttpServer/tests/", - "Mine\\SecurityBundle\\Tests\\": "src/SecurityBundle/tests/", - "Mine\\Security\\Access\\Tests\\": "src/SecurityAccess/tests/", - "Mine\\Security\\Http\\Tests\\": "src/SecurityHttp/tests/", "Mine\\Support\\Tests\\": "src/Support/tests/", "Mine\\Tests\\": "tests/", "Mine\\Translatable\\": "src/translatable/tests/" @@ -152,8 +143,6 @@ "Mine\\Generator\\GeneratorConfigProvider", "Mine\\Generator\\GeneratorConfigProvider", "Mine\\HttpServer\\ConfigProvider", - "Mine\\SecurityBundle\\ConfigProvider", - "Mine\\Security\\Http\\ConfigProvider", "Mine\\Support\\ConfigProvider", "Mine\\Translatable\\ConfigProvider", "Xmo\\JWTAuth\\ConfigProvider" diff --git a/src/SecurityAccess/.gitattributes b/src/SecurityAccess/.gitattributes deleted file mode 100644 index ce244ab1..00000000 --- a/src/SecurityAccess/.gitattributes +++ /dev/null @@ -1,2 +0,0 @@ -/tests export-ignore -/.github export-ignore \ No newline at end of file diff --git a/src/SecurityAccess/.github/workflows/close-pull-request.yml b/src/SecurityAccess/.github/workflows/close-pull-request.yml deleted file mode 100644 index c8182b84..00000000 --- a/src/SecurityAccess/.github/workflows/close-pull-request.yml +++ /dev/null @@ -1,13 +0,0 @@ -name: Close Pull Request - -on: - pull_request_target: - types: [ opened ] - -jobs: - run: - runs-on: ubuntu-latest - steps: - - uses: superbrothers/close-pull-request@v3 - with: - comment: "Hi, this is a READ-ONLY repository, please submit your PR on the https://github.com/mineadmin/components repository.

This Pull Request will close automatically.

Thanks! " \ No newline at end of file diff --git a/src/SecurityAccess/.github/workflows/release.yml b/src/SecurityAccess/.github/workflows/release.yml deleted file mode 100644 index 2fc8404b..00000000 --- a/src/SecurityAccess/.github/workflows/release.yml +++ /dev/null @@ -1,24 +0,0 @@ -on: - push: - tags: - - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10 - -name: Release - -jobs: - release: - name: Release - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - - name: Create Release - id: create_release - uses: actions/create-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - tag_name: ${{ github.ref }} - release_name: Release ${{ github.ref }} - draft: false - prerelease: false \ No newline at end of file diff --git a/src/SecurityAccess/LICENSE b/src/SecurityAccess/LICENSE deleted file mode 100644 index c5722457..00000000 --- a/src/SecurityAccess/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2024 MineAdmin - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/src/SecurityAccess/README.md b/src/SecurityAccess/README.md deleted file mode 100644 index 307e2a5a..00000000 --- a/src/SecurityAccess/README.md +++ /dev/null @@ -1 +0,0 @@ -# 基于 mineadmin/security-bundle 提供安全访问控制 \ No newline at end of file diff --git a/src/SecurityAccess/composer.json b/src/SecurityAccess/composer.json deleted file mode 100644 index ca6c9968..00000000 --- a/src/SecurityAccess/composer.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "name": "mineadmin/security-access", - "description": "Security Access Component", - "license": "MIT", - "type": "library", - "authors": [ - { - "name": "xmo", - "email": "root@imoi.cn", - "role": "Developer" - }, - { - "name": "zds", - "email": "2771717608@qq.com", - "role": "Developer" - } - ], - "require": { - "php": ">=8.1", - "casbin/casbin": "^3.21", - "friendsofhyperf/facade": "^3.1", - "mineadmin/security-bundle": "2.0.x-dev" - }, - "autoload": { - "psr-4": { - "Mine\\Security\\Access\\": "src/" - } - }, - "autoload-dev": { - "psr-4": { - "Mine\\Security\\Access\\Tests\\": "tests/" - } - }, - "extra": { - "config": { - "hyperf": "Mine\\Security\\Access\\ConfigProvider" - } - } -} \ No newline at end of file diff --git a/src/SecurityAccess/publish/access.php b/src/SecurityAccess/publish/access.php deleted file mode 100644 index 74b12829..00000000 --- a/src/SecurityAccess/publish/access.php +++ /dev/null @@ -1,25 +0,0 @@ - 'rbac', - 'component' => [ - 'rbac' => [ - 'construct' => [ - __DIR__ . '/rbac_model.conf', - __DIR__ . '/rbac_policy.csv', - ], - 'enforcer' => Enforcer::class, - ], - ], -]; diff --git a/src/SecurityAccess/publish/rbac_model.conf b/src/SecurityAccess/publish/rbac_model.conf deleted file mode 100644 index 71159e38..00000000 --- a/src/SecurityAccess/publish/rbac_model.conf +++ /dev/null @@ -1,14 +0,0 @@ -[request_definition] -r = sub, obj, act - -[policy_definition] -p = sub, obj, act - -[role_definition] -g = _, _ - -[policy_effect] -e = some(where (p.eft == allow)) - -[matchers] -m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act \ No newline at end of file diff --git a/src/SecurityAccess/publish/rbac_policy.csv b/src/SecurityAccess/publish/rbac_policy.csv deleted file mode 100644 index e69de29b..00000000 diff --git a/src/SecurityAccess/src/ConfigProvider.php b/src/SecurityAccess/src/ConfigProvider.php deleted file mode 100644 index a93c4730..00000000 --- a/src/SecurityAccess/src/ConfigProvider.php +++ /dev/null @@ -1,47 +0,0 @@ - [ - Access::class => Manager::class, - ], - 'publish' => [ - [ - 'id' => 'access rbac conf', - 'description' => 'Access Rbac Conf', - 'source' => __DIR__ . '/../publish/access.php', - 'destination' => BASE_PATH . '/config/autoload/access.php', - ], - [ - 'id' => 'access rbac model conf', - 'description' => 'Access Rbac Model Conf', - 'source' => __DIR__ . '/../publish/rbac_model.conf', - 'destination' => BASE_PATH . '/config/autoload/rbac_model.conf', - ], - [ - 'id' => 'access rbac policy csv', - 'description' => 'Access Rbac Policy csv', - 'source' => __DIR__ . '/../publish/rbac_policy.csv', - 'destination' => BASE_PATH . '/config/autoload/rbac_policy.csv', - ], - ], - ]; - } -} diff --git a/src/SecurityAccess/src/Contract/Access.php b/src/SecurityAccess/src/Contract/Access.php deleted file mode 100644 index c279ffe5..00000000 --- a/src/SecurityAccess/src/Contract/Access.php +++ /dev/null @@ -1,24 +0,0 @@ -getConfig('default'); - } - return $this->getAdapter($name); - } - - protected function getConfig(string $key, mixed $default = null): mixed - { - return $this->config->get('access.' . $key, $default); - } - - protected function getAdapter(string $name): Enforcer - { - $adapter = $this->getConfig('component.' . $name); - if (empty($adapter)) { - throw new AccessException(sprintf('Access adapter [%s] not exists.', $name)); - } - if (empty($adapter['construct']) || empty($adapter['enforcer'])) { - throw new AccessException(sprintf('Access adapter [%s] construct or enforcer not exists.', $name)); - } - $construct = $adapter['construct']; - $enforcer = $adapter['enforcer']; - return new $enforcer(...$construct); - } -} diff --git a/src/SecurityAccess/src/Rbac.php b/src/SecurityAccess/src/Rbac.php deleted file mode 100644 index 11b6b7a0..00000000 --- a/src/SecurityAccess/src/Rbac.php +++ /dev/null @@ -1,36 +0,0 @@ -getAccess()->get('rbac')->{$name}(...$arguments); - } - - public function getAccess(): Access - { - return $this->access; - } -} diff --git a/src/SecurityAccess/src/RbacFacade.php b/src/SecurityAccess/src/RbacFacade.php deleted file mode 100644 index 49a6b86f..00000000 --- a/src/SecurityAccess/src/RbacFacade.php +++ /dev/null @@ -1,26 +0,0 @@ -assertIsArray((new ConfigProvider())()); - } -} diff --git a/src/SecurityAccess/tests/Cases/ManagerTest.php b/src/SecurityAccess/tests/Cases/ManagerTest.php deleted file mode 100644 index 5d02516b..00000000 --- a/src/SecurityAccess/tests/Cases/ManagerTest.php +++ /dev/null @@ -1,112 +0,0 @@ -config = \Mockery::mock(Config::class); - $this->manager = new Manager($this->config); - } - - public function testGetWithoutName(): void - { - // Set up the expected default value from the config - $expectedDefault = 'rbac'; - $this->config->allows('get') - ->with('access.default', null) - ->andReturn($expectedDefault); - $this->config->allows('get') - ->with('access.component.rbac', null) - ->andReturn([ - 'construct' => [ - dirname(__DIR__, 2) . '/publish/rbac_model.conf', - dirname(__DIR__, 2) . '/publish/rbac_policy.csv', - ], - 'enforcer' => Enforcer::class, - ]); - - // Call the get method without passing a name - $enforcer = $this->manager->get(); - - // Assert that the getAdapter method is called with the expected default value - $this->assertInstanceOf(Enforcer::class, $enforcer); - } - - public function testGetWithName(): void - { - // Set up the expected adapter name and config - $adapterName = 'customAdapter'; - $adapterConfig = [ - 'construct' => [ - dirname(__DIR__, 2) . '/publish/rbac_model.conf', - dirname(__DIR__, 2) . '/publish/rbac_policy.csv', - ], - 'enforcer' => Enforcer::class, - ]; - $this->config->allows('get') - ->with('access.component.' . $adapterName, null) - ->andReturn($adapterConfig); - - // Call the get method with the adapter name - $enforcer = $this->manager->get($adapterName); - - // Assert that the getAdapter method is called with the expected adapter name - $this->assertInstanceOf(Enforcer::class, $enforcer); - } - - public function testGetAdapterWithNonExistentAdapter(): void - { - // Set up the expected adapter name and return null for the config - $adapterName = 'nonExistentAdapter'; - $this->config->allows('get') - ->with('access.component.' . $adapterName, null) - ->andReturn(null); - - // Assert that an AccessException is thrown when the adapter does not exist - $this->expectException(AccessException::class); - $this->manager->get($adapterName); - } - - public function testGetAdapterWithMissingConstructOrEnforcer(): void - { - // Set up the expected adapter name and incomplete config - $adapterName = 'incompleteAdapter'; - $adapterConfig = [ - 'construct' => [], - 'enforcer' => 'IncompleteEnforcerClass', - ]; - $this->config->allows('get') - ->with('access.component.' . $adapterName, null) - ->andReturn($adapterConfig); - - // Assert that an AccessException is thrown when the adapter construct or enforcer is missing - $this->expectException(AccessException::class); - $this->manager->get($adapterName); - } -} diff --git a/src/SecurityAccess/tests/Cases/RbacFacadeTest.php b/src/SecurityAccess/tests/Cases/RbacFacadeTest.php deleted file mode 100644 index 34611da8..00000000 --- a/src/SecurityAccess/tests/Cases/RbacFacadeTest.php +++ /dev/null @@ -1,29 +0,0 @@ -assertEquals(Rbac::class, RbacFacade::getFacadeRoot()); - } -} diff --git a/src/SecurityAccess/tests/Cases/RbacTest.php b/src/SecurityAccess/tests/Cases/RbacTest.php deleted file mode 100644 index e6adfb4c..00000000 --- a/src/SecurityAccess/tests/Cases/RbacTest.php +++ /dev/null @@ -1,50 +0,0 @@ -enforcer = \Mockery::mock(Enforcer::class); - - $accessMock->allows('get') - ->with('rbac') - ->andReturn($this->enforcer); - - $this->rbac = new Rbac($accessMock); - } - - public function testCallMethod(): void - { - $methodName = 'testMethod'; - $arguments = ['arg1', 'arg2']; - $this->enforcer->allows($methodName)->with(...$arguments)->andReturn('result'); - $res = $this->rbac->{$methodName}(...$arguments); - $this->assertEquals('result', $res); - } -} diff --git a/src/SecurityBundle/.gitattributes b/src/SecurityBundle/.gitattributes deleted file mode 100644 index ce244ab1..00000000 --- a/src/SecurityBundle/.gitattributes +++ /dev/null @@ -1,2 +0,0 @@ -/tests export-ignore -/.github export-ignore \ No newline at end of file diff --git a/src/SecurityBundle/.github/workflows/close-pull-request.yml b/src/SecurityBundle/.github/workflows/close-pull-request.yml deleted file mode 100644 index c8182b84..00000000 --- a/src/SecurityBundle/.github/workflows/close-pull-request.yml +++ /dev/null @@ -1,13 +0,0 @@ -name: Close Pull Request - -on: - pull_request_target: - types: [ opened ] - -jobs: - run: - runs-on: ubuntu-latest - steps: - - uses: superbrothers/close-pull-request@v3 - with: - comment: "Hi, this is a READ-ONLY repository, please submit your PR on the https://github.com/mineadmin/components repository.

This Pull Request will close automatically.

Thanks! " \ No newline at end of file diff --git a/src/SecurityBundle/.github/workflows/release.yml b/src/SecurityBundle/.github/workflows/release.yml deleted file mode 100644 index 2fc8404b..00000000 --- a/src/SecurityBundle/.github/workflows/release.yml +++ /dev/null @@ -1,24 +0,0 @@ -on: - push: - tags: - - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10 - -name: Release - -jobs: - release: - name: Release - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - - name: Create Release - id: create_release - uses: actions/create-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - tag_name: ${{ github.ref }} - release_name: Release ${{ github.ref }} - draft: false - prerelease: false \ No newline at end of file diff --git a/src/SecurityBundle/LICENSE b/src/SecurityBundle/LICENSE deleted file mode 100644 index c5722457..00000000 --- a/src/SecurityBundle/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2024 MineAdmin - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/src/SecurityBundle/README.md b/src/SecurityBundle/README.md deleted file mode 100644 index b931c8ab..00000000 --- a/src/SecurityBundle/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Hyperf Security Bundle - -像 Symfony/security 那样提供用户认证、授权、协程/请求安全上下文等功能 \ No newline at end of file diff --git a/src/SecurityBundle/composer.json b/src/SecurityBundle/composer.json deleted file mode 100644 index 385045ea..00000000 --- a/src/SecurityBundle/composer.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "name": "mineadmin/security-bundle", - "description": "MineAdmin Security bundle,类似 Symfony/Security 组件,提供用户认证、授权、安全上下文等功能。", - "license": "MIT", - "type": "library", - "authors": [ - { - "name": "xmo", - "email": "root@imoi.cn", - "role": "Developer" - }, - { - "name": "zds", - "email": "2771717608@qq.com", - "role": "Developer" - } - ], - "require": { - "php": ">=8.1", - "friendsofhyperf/encryption": "^3.1", - "hyperf/framework": "^3.1" - }, - "autoload": { - "psr-4": { - "Mine\\SecurityBundle\\": "src" - } - }, - "autoload-dev": { - "psr-4": { - "Mine\\SecurityBundle\\Tests\\": "tests" - } - }, - "extra": { - "hyperf": { - "config": "Mine\\SecurityBundle\\ConfigProvider" - } - } -} \ No newline at end of file diff --git a/src/SecurityBundle/src/AbstractUserProvider.php b/src/SecurityBundle/src/AbstractUserProvider.php deleted file mode 100644 index bf9b2311..00000000 --- a/src/SecurityBundle/src/AbstractUserProvider.php +++ /dev/null @@ -1,100 +0,0 @@ -where($user->getRememberTokenName(), $token)->first(); - }, $this->getUserEntity()->getSecurityBuilder(), $this->getUserEntity(), $token); - } - - public function updateRememberToken(UserInterface $user, string $token): bool - { - return value(function (Builder $builder, UserInterface $user, string $token) { - return $builder->update([ - $user->getRememberTokenName() => $token, - ]); - }, $user->getSecurityBuilder(), $user, $token); - } - - public function retrieveById(mixed $identifier): ?object - { - return value( - function (Builder $builder, UserInterface $entity, mixed $identifier) { - return $builder->where($entity->getIdentifierName(), $identifier)->first(); - }, - $this->getUserEntity()->getSecurityBuilder(), - $this->getUserEntity(), - $identifier - ); - } - - public function credentials(array $credentials): false|UserInterface - { - $userEntity = $this->getUserEntity(); - $builder = $userEntity->getSecurityBuilder(); - $identifierName = $userEntity->getIdentifierName(); - if (isset($credentials[$identifierName])) { - /** - * @var UserInterface $entity - */ - $entity = $builder->where($identifierName, $credentials[$identifierName])->first(); - if ($entity === null) { - return false; - } - if ($this->verifyPassword($entity, $credentials['password'])) { - $this->dispatcher->dispatch(new Login($entity)); - return $entity; - } - } - return false; - } - - protected function verifyPassword(UserInterface $user, string $password): bool - { - if (password_verify($password, $user->getPassword())) { - $this->dispatcher->dispatch(new Verified($user)); - return true; - } - $this->dispatcher->dispatch(new Validated($user)); - return false; - } - - protected function getUserEntity(): UserInterface - { - $entityClass = $this->config->get('entity', '\App\Model\User'); - if (! class_exists($entityClass)) { - new NotFoundUserEntityException(); - } - return new $entityClass(); - } -} diff --git a/src/SecurityBundle/src/Config.php b/src/SecurityBundle/src/Config.php deleted file mode 100644 index 46d67fa9..00000000 --- a/src/SecurityBundle/src/Config.php +++ /dev/null @@ -1,29 +0,0 @@ -config->get(self::PREFIX . '.' . $key, $default); - } -} diff --git a/src/SecurityBundle/src/ConfigProvider.php b/src/SecurityBundle/src/ConfigProvider.php deleted file mode 100644 index ca30582c..00000000 --- a/src/SecurityBundle/src/ConfigProvider.php +++ /dev/null @@ -1,21 +0,0 @@ -user; - } -} diff --git a/src/SecurityBundle/src/Event/Authenticated.php b/src/SecurityBundle/src/Event/Authenticated.php deleted file mode 100644 index 38947ae1..00000000 --- a/src/SecurityBundle/src/Event/Authenticated.php +++ /dev/null @@ -1,27 +0,0 @@ -user; - } -} diff --git a/src/SecurityBundle/src/Event/Failed.php b/src/SecurityBundle/src/Event/Failed.php deleted file mode 100644 index f29b80fd..00000000 --- a/src/SecurityBundle/src/Event/Failed.php +++ /dev/null @@ -1,27 +0,0 @@ -user; - } -} diff --git a/src/SecurityBundle/src/Event/Login.php b/src/SecurityBundle/src/Event/Login.php deleted file mode 100644 index 7576a6da..00000000 --- a/src/SecurityBundle/src/Event/Login.php +++ /dev/null @@ -1,27 +0,0 @@ -user; - } -} diff --git a/src/SecurityBundle/src/Event/Logout.php b/src/SecurityBundle/src/Event/Logout.php deleted file mode 100644 index 8f974b11..00000000 --- a/src/SecurityBundle/src/Event/Logout.php +++ /dev/null @@ -1,27 +0,0 @@ -user; - } -} diff --git a/src/SecurityBundle/src/Event/PasswordReset.php b/src/SecurityBundle/src/Event/PasswordReset.php deleted file mode 100644 index ca484c91..00000000 --- a/src/SecurityBundle/src/Event/PasswordReset.php +++ /dev/null @@ -1,15 +0,0 @@ -user; - } -} diff --git a/src/SecurityBundle/src/Event/Verified.php b/src/SecurityBundle/src/Event/Verified.php deleted file mode 100644 index 2539b1bb..00000000 --- a/src/SecurityBundle/src/Event/Verified.php +++ /dev/null @@ -1,27 +0,0 @@ -user; - } -} diff --git a/src/SecurityBundle/src/Exception/NotFoundUserEntityException.php b/src/SecurityBundle/src/Exception/NotFoundUserEntityException.php deleted file mode 100644 index 2f72dea5..00000000 --- a/src/SecurityBundle/src/Exception/NotFoundUserEntityException.php +++ /dev/null @@ -1,15 +0,0 @@ -container->get($this->config->get('token')); - } - - public function getContext(): ContextInterface - { - return $this->container->get($this->config->get('context')); - } - - public function getUserProvider(): UserProviderInterface - { - return $this->container->get($this->config->get('user_provider')); - } -} diff --git a/src/SecurityBundle/tests/AbstractUserProviderTest.php b/src/SecurityBundle/tests/AbstractUserProviderTest.php deleted file mode 100644 index d308c787..00000000 --- a/src/SecurityBundle/tests/AbstractUserProviderTest.php +++ /dev/null @@ -1,227 +0,0 @@ -set(ConfigInterface::class, new \Hyperf\Config\Config([ - 'encryption' => [ - 'key' => 'base64:MhEHk72OcV2ttAljUu9Caaam3iP2BnGcwb6GWKkUfV4=', - 'cipher' => 'AES-256-CBC', - ], - ])); - } - - public function testConstruct(): void - { - $instance = new class(\Mockery::mock(EventDispatcherInterface::class), \Mockery::mock(Config::class)) extends AbstractUserProvider { - public function retrieveByCredentials(array $credentials): ?object - { - return null; - } - - public function validateCredentials(UserInterface $user, array $credentials): bool - { - return false; - } - }; - $this->assertInstanceOf(AbstractUserProvider::class, $instance); - } - - public function testCredentials() - { - $event = \Mockery::mock(EventDispatcherInterface::class); - $config = \Mockery::mock(Config::class); - $config->allows('get') - ->with('entity', '\App\Model\User') - ->andReturn(UserModel::class); - $builder = \Mockery::mock(Builder::class); - $verifyModel = new UserModel(); - $verifyModel->setPassword(password_hash('xxxxxx', PASSWORD_DEFAULT)); - $builder->allows('first')->andReturn(null, new UserModel(), $verifyModel); - $builder->allows('where') - ->andReturnUsing(function ($column, $value) use ($builder) { - if ($column === 'email' && $value === 'zds@qq.com') { - return $builder; - } - }); - ApplicationContext::getContainer()->set('mocker.builder', $builder); - $instance = new class($event, $config) extends AbstractUserProvider { - public function retrieveByCredentials(array $credentials): ?object - { - return null; - } - - public function validateCredentials(UserInterface $user, array $credentials): bool - { - return false; - } - }; - $this->assertFalse($instance->credentials([ - 'email' => 'zds@qq.com', - 'password' => '123456', - ])); - $event->allows('dispatch')->andReturnUsing(function ($event) { - $this->assertInstanceOf(Validated::class, $event); - }, function ($event) { - if ($event instanceof Verified) { - $this->assertInstanceOf(Verified::class, $event); - } else { - $this->assertInstanceOf(Login::class, $event); - } - }); - - $this->assertFalse($instance->credentials([ - 'email' => 'zds@qq.com', - 'password' => '123456', - ])); - - $this->assertInstanceOf(UserInterface::class, $instance->credentials([ - 'email' => 'zds@qq.com', - 'password' => 'xxxxxx', - ])); - } - - public function testRetrieveById(): void - { - $event = \Mockery::mock(EventDispatcherInterface::class); - $config = \Mockery::mock(Config::class); - $config->allows('get') - ->with('entity', '\App\Model\User') - ->andReturn(UserModel::class); - $builder = \Mockery::mock(Builder::class); - $verifyModel = new UserModel(); - $verifyModel->setPassword(password_hash('xxxxxx', PASSWORD_DEFAULT)); - $builder->allows('first')->andReturn(null, new UserModel(), $verifyModel); - $builder->allows('where') - ->andReturnUsing(function ($column, $value) use ($builder) { - if ($column === 'email') { - return $builder; - } - }); - ApplicationContext::getContainer()->set('mocker.builder', $builder); - $instance = new class($event, $config) extends AbstractUserProvider { - public function retrieveByCredentials(array $credentials): ?object - { - return null; - } - - public function validateCredentials(UserInterface $user, array $credentials): bool - { - return false; - } - }; - $this->assertNull($instance->retrieveById(1)); - $this->assertInstanceOf(UserInterface::class, $instance->retrieveById(2)); - } - - public function testUpdateRememberToken(): void - { - $event = \Mockery::mock(EventDispatcherInterface::class); - $config = \Mockery::mock(Config::class); - $config->allows('get') - ->with('entity', '\App\Model\User') - ->andReturn(UserModel::class); - $builder = \Mockery::mock(Builder::class); - $verifyModel = new UserModel(); - $verifyModel->setPassword(password_hash('xxxxxx', PASSWORD_DEFAULT)); - $builder->allows('update')->andReturnUsing(function ($data) { - if ($data['remember_token'] === '123456') { - return true; - } - - return false; - }); - ApplicationContext::getContainer()->set('mocker.builder', $builder); - $instance = new class($event, $config) extends AbstractUserProvider { - public function retrieveByCredentials(array $credentials): ?object - { - return null; - } - - public function validateCredentials(UserInterface $user, array $credentials): bool - { - return false; - } - }; - $this->assertTrue($instance->updateRememberToken(new UserModel(), '123456')); - $this->assertFalse($instance->updateRememberToken(new UserModel(), 'xxx')); - } - - public function testRetrieveByToken(): void - { - $event = \Mockery::mock(EventDispatcherInterface::class); - $config = \Mockery::mock(Config::class); - $config->allows('get') - ->with('entity', '\App\Model\User') - ->andReturn(UserModel::class); - $builder = \Mockery::mock(Builder::class); - $verifyModel = new UserModel(); - $verifyModel->setPassword(password_hash('xxxxxx', PASSWORD_DEFAULT)); - $builder->allows('update')->andReturnUsing(function ($data) { - if ($data['remember_token'] === '123456') { - return true; - } - - return false; - }); - $builder->allows('where')->andReturnUsing(function ($column, $value) use ($builder) { - $this->assertEquals('remember_token', $column); - $this->assertEquals('123456', $value); - return $builder; - }, function ($column, $value) use ($builder) { - $this->assertEquals('remember_token', $column); - $this->assertNotEquals('123456', $value); - return $builder; - }); - $builder->allows('first')->andReturn(new UserModel(), null); - ApplicationContext::getContainer()->set('mocker.builder', $builder); - $instance = new class($event, $config) extends AbstractUserProvider { - public function retrieveByCredentials(array $credentials): ?object - { - return null; - } - - public function validateCredentials(UserInterface $user, array $credentials): bool - { - return false; - } - }; - $this->assertInstanceOf(UserInterface::class, $instance->retrieveByToken('123456')); - $this->assertNull($instance->retrieveByToken('11111')); - } -} diff --git a/src/SecurityBundle/tests/ConfigProviderTest.php b/src/SecurityBundle/tests/ConfigProviderTest.php deleted file mode 100644 index f3e09776..00000000 --- a/src/SecurityBundle/tests/ConfigProviderTest.php +++ /dev/null @@ -1,28 +0,0 @@ -assertIsArray((new ConfigProvider())()); - } -} diff --git a/src/SecurityBundle/tests/ConfigTest.php b/src/SecurityBundle/tests/ConfigTest.php deleted file mode 100644 index 5e5f3d4b..00000000 --- a/src/SecurityBundle/tests/ConfigTest.php +++ /dev/null @@ -1,32 +0,0 @@ -allows('get')->with('security.xxx', null)->andReturn('xxx'); - $config = new Config($mock); - $this->assertEquals('xxx', $config->get('xxx')); - } -} diff --git a/src/SecurityBundle/tests/Context/ConTextTest.php b/src/SecurityBundle/tests/Context/ConTextTest.php deleted file mode 100644 index 56e3ec6f..00000000 --- a/src/SecurityBundle/tests/Context/ConTextTest.php +++ /dev/null @@ -1,41 +0,0 @@ -set('key', 'value'); - $this->assertEquals('value', $context->get('key')); - $this->assertEquals(null, $context->get('not_exist')); - $this->assertEquals('xxx', $context->get('not_exist', 'xxx')); - $this->assertEquals('xxx', $context->getOrSet('not_exist', 'xxx')); - $this->assertEquals('xxx2', $context->getOrSet('not_exist1', function () { - return 'xxx2'; - })); - $this->assertTrue($context->has('key')); - $this->assertFalse($context->has('key2')); - } -} diff --git a/src/SecurityBundle/tests/SecurityTest.php b/src/SecurityBundle/tests/SecurityTest.php deleted file mode 100644 index 251379ef..00000000 --- a/src/SecurityBundle/tests/SecurityTest.php +++ /dev/null @@ -1,49 +0,0 @@ -assertInstanceOf(Security::class, new Security($config, ApplicationContext::getContainer())); - } - - public function testGet() - { - $config = \Mockery::mock(Config::class); - $config->allows('get')->andReturn('xxx'); - $container = \Mockery::mock(ContainerInterface::class); - $security = new Security($config, $container); - $container->allows('get') - ->andReturn(\Mockery::mock(TokenInterface::class), \Mockery::mock(UserProviderInterface::class), \Mockery::mock(ContextInterface::class)); - $this->assertInstanceOf(TokenInterface::class, $security->getToken()); - $this->assertInstanceOf(UserProviderInterface::class, $security->getUserProvider()); - $this->assertInstanceOf(ContextInterface::class, $security->getContext()); - } -} diff --git a/src/SecurityBundle/tests/Stub/UserModel.php b/src/SecurityBundle/tests/Stub/UserModel.php deleted file mode 100644 index b98f4d07..00000000 --- a/src/SecurityBundle/tests/Stub/UserModel.php +++ /dev/null @@ -1,61 +0,0 @@ -remember_token; - } - - public function setRememberToken(string $token): void - { - $this->remember_token = $token; - } - - public function getRememberTokenName(): string - { - return 'remember_token'; - } - - public function getPassword(): string - { - return $this->attributes['password'] ?? '123456'; - } - - public function setPassword(string $password): void - { - $this->attributes['password'] = $password; - } - - public function getSecurityBuilder(): Builder - { - return ApplicationContext::getContainer()->get('mocker.builder'); - } -} diff --git a/src/SecurityHttp/.gitattributes b/src/SecurityHttp/.gitattributes deleted file mode 100644 index ce244ab1..00000000 --- a/src/SecurityHttp/.gitattributes +++ /dev/null @@ -1,2 +0,0 @@ -/tests export-ignore -/.github export-ignore \ No newline at end of file diff --git a/src/SecurityHttp/.github/workflows/close-pull-request.yml b/src/SecurityHttp/.github/workflows/close-pull-request.yml deleted file mode 100644 index c8182b84..00000000 --- a/src/SecurityHttp/.github/workflows/close-pull-request.yml +++ /dev/null @@ -1,13 +0,0 @@ -name: Close Pull Request - -on: - pull_request_target: - types: [ opened ] - -jobs: - run: - runs-on: ubuntu-latest - steps: - - uses: superbrothers/close-pull-request@v3 - with: - comment: "Hi, this is a READ-ONLY repository, please submit your PR on the https://github.com/mineadmin/components repository.

This Pull Request will close automatically.

Thanks! " \ No newline at end of file diff --git a/src/SecurityHttp/.github/workflows/release.yml b/src/SecurityHttp/.github/workflows/release.yml deleted file mode 100644 index 2fc8404b..00000000 --- a/src/SecurityHttp/.github/workflows/release.yml +++ /dev/null @@ -1,24 +0,0 @@ -on: - push: - tags: - - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10 - -name: Release - -jobs: - release: - name: Release - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - - name: Create Release - id: create_release - uses: actions/create-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - tag_name: ${{ github.ref }} - release_name: Release ${{ github.ref }} - draft: false - prerelease: false \ No newline at end of file diff --git a/src/SecurityHttp/LICENSE b/src/SecurityHttp/LICENSE deleted file mode 100644 index c5722457..00000000 --- a/src/SecurityHttp/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2024 MineAdmin - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/src/SecurityHttp/README.md b/src/SecurityHttp/README.md deleted file mode 100644 index b33c3869..00000000 --- a/src/SecurityHttp/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# MineAdmin security http component - -内置了一套基础的 jwt 认证机制,可以用于简单的 api 接口的认证。 \ No newline at end of file diff --git a/src/SecurityHttp/composer.json b/src/SecurityHttp/composer.json deleted file mode 100644 index 3da3e36a..00000000 --- a/src/SecurityHttp/composer.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "name": "mineadmin/security-http", - "description": "MineAdmin Security http component", - "license": "MIT", - "type": "library", - "authors": [ - { - "name": "xmo", - "email": "root@imoi.cn", - "role": "Developer" - }, - { - "name": "zds", - "email": "2771717608@qq.com", - "role": "Developer" - } - ], - "require": { - "php": ">=8.1", - "lcobucci/jwt": "^5.2", - "mineadmin/http-server": "2.0.x-dev", - "mineadmin/security-bundle": "2.0.x-dev" - }, - "autoload": { - "psr-4": { - "Mine\\Security\\Http\\": "src/" - } - }, - "autoload-dev": { - "psr-4": { - "Mine\\Security\\Http\\Tests\\": "tests/" - } - }, - "extra": { - "hyperf": { - "config": "Mine\\Security\\Http\\ConfigProvider" - } - } -} \ No newline at end of file diff --git a/src/SecurityHttp/publish/security.php b/src/SecurityHttp/publish/security.php deleted file mode 100644 index 1df5f128..00000000 --- a/src/SecurityHttp/publish/security.php +++ /dev/null @@ -1,144 +0,0 @@ - UserProvider::class, - 'token' => Token::class, - /* - * entity class - */ - 'entity' => 'App\Models\User', - /* - * An object that attempts to provide a security context - */ - 'context' => Context::class, - 'jwt' => [ - 'login_type' => env('JWT_LOGIN_TYPE', 'mpop'), // 登录方式,sso为单点登录,mpop为多点登录 - - /* - * 单点登录自定义数据中必须存在uid的键值,这个key你可以自行定义,只要自定义数据中存在该键即可 - */ - 'sso_key' => 'uid', - - 'secret' => env('JWT_SECRET', 'phper666'), // 非对称加密使用字符串,请使用自己加密的字符串 - - /* - * JWT 权限keys - * 对称算法: HS256, HS384 & HS512 使用 `JWT_SECRET`. - * 非对称算法: RS256, RS384 & RS512 / ES256, ES384 & ES512 使用下面的公钥私钥. - */ - 'keys' => [ - 'public' => env('JWT_PUBLIC_KEY'), // 公钥,例如:'file:///path/to/public/key' - 'private' => env('JWT_PRIVATE_KEY'), // 私钥,例如:'file:///path/to/private/key' - ], - - 'ttl' => env('JWT_TTL', 7200), // token过期时间,单位为秒 - - 'alg' => env('JWT_ALG', 'HS256'), // jwt的hearder加密算法 - - /* - * 支持的算法 - */ - 'supported_algs' => [ - 'HS256' => 'Lcobucci\JWT\Signer\Hmac\Sha256', - 'HS384' => 'Lcobucci\JWT\Signer\Hmac\Sha384', - 'HS512' => 'Lcobucci\JWT\Signer\Hmac\Sha512', - 'ES256' => 'Lcobucci\JWT\Signer\Ecdsa\Sha256', - 'ES384' => 'Lcobucci\JWT\Signer\Ecdsa\Sha384', - 'ES512' => 'Lcobucci\JWT\Signer\Ecdsa\Sha512', - 'RS256' => 'Lcobucci\JWT\Signer\Rsa\Sha256', - 'RS384' => 'Lcobucci\JWT\Signer\Rsa\Sha384', - 'RS512' => 'Lcobucci\JWT\Signer\Rsa\Sha512', - ], - - /* - * 对称算法名称 - */ - 'symmetry_algs' => [ - 'HS256', - 'HS384', - 'HS512', - ], - - /* - * 非对称算法名称 - */ - 'asymmetric_algs' => [ - 'RS256', - 'RS384', - 'RS512', - 'ES256', - 'ES384', - 'ES512', - ], - - /* - * 是否开启黑名单,单点登录和多点登录的注销、刷新使原token失效,必须要开启黑名单,目前黑名单缓存只支持hyperf缓存驱动 - */ - 'blacklist_enabled' => env('JWT_BLACKLIST_ENABLED', true), - - /* - * 黑名单的宽限时间 单位为:秒,注意:如果使用单点登录,该宽限时间无效 - */ - 'blacklist_grace_period' => env('JWT_BLACKLIST_GRACE_PERIOD', 0), - - /* - * 黑名单缓存token时间,注意:该时间一定要设置比token过期时间要大一点,默认为1天,最好设置跟过期时间一样 - */ - 'blacklist_cache_ttl' => env('JWT_TTL', 86400), - - 'blacklist_prefix' => 'mineadmin_jwt', // 黑名单缓存的前缀 - 'black' => CacheBlack::class, // 黑名单实现类,默认使用hyperf缓存驱动 - - /* - * 区分不同场景的token,比如你一个项目可能会有多种类型的应用接口鉴权,下面自行定义,我只是举例子 - * 下面的配置会自动覆盖根配置,比如application1会里面的数据会覆盖掉根数据 - * 下面的scene会和根数据合并 - * scene必须存在一个default - * 什么叫根数据,这个配置的一维数组,除了scene都叫根配置 - */ - 'scene' => [ - 'default' => [], - 'application1' => [ - 'secret' => 'application1', // 非对称加密使用字符串,请使用自己加密的字符串 - 'login_type' => 'sso', // 登录方式,sso为单点登录,mpop为多点登录 - 'sso_key' => 'uid', - 'ttl' => 7200, // token过期时间,单位为秒 - 'blacklist_cache_ttl' => env('JWT_TTL', 7200), // 黑名单缓存token时间,注意:该时间一定要设置比token过期时间要大一点,默认为100秒,最好设置跟过期时间一样 - ], - 'application2' => [ - 'secret' => 'application2', // 非对称加密使用字符串,请使用自己加密的字符串 - 'login_type' => 'sso', // 登录方式,sso为单点登录,mpop为多点登录 - 'sso_key' => 'uid', - 'ttl' => 7200, // token过期时间,单位为秒 - 'blacklist_cache_ttl' => env('JWT_TTL', 7200), // 黑名单缓存token时间,注意:该时间一定要设置比token过期时间要大一点,默认为100秒,最好设置跟过期时间一样 - ], - 'application3' => [ - 'secret' => 'application3', // 非对称加密使用字符串,请使用自己加密的字符串 - 'login_type' => 'mppo', // 登录方式,sso为单点登录,mpop为多点登录 - 'ttl' => 7200, // token过期时间,单位为秒 - 'blacklist_cache_ttl' => env('JWT_TTL', 7200), // 黑名单缓存token时间,注意:该时间一定要设置比token过期时间要大一点,默认为100秒,最好设置跟过期时间一样 - ], - ], - // 是否验证当前场景配置是否是生成当前的token的配置,需要配合自定义中间件实现,false会根据当前token拿到原来的场景配置,并且验证当前token - 'independentTokenVerify' => false, - ], -]; diff --git a/src/SecurityHttp/src/Aspect/CurrentUserAspect.php b/src/SecurityHttp/src/Aspect/CurrentUserAspect.php deleted file mode 100644 index a329cb1e..00000000 --- a/src/SecurityHttp/src/Aspect/CurrentUserAspect.php +++ /dev/null @@ -1,29 +0,0 @@ -process(); - } -} diff --git a/src/SecurityHttp/src/Attribute/CurrentUser.php b/src/SecurityHttp/src/Attribute/CurrentUser.php deleted file mode 100644 index 087d5482..00000000 --- a/src/SecurityHttp/src/Attribute/CurrentUser.php +++ /dev/null @@ -1,23 +0,0 @@ -getSecretName(); - $value = $this->generator(); - $envPath = $this->getEnvPath(); - - if (! file_exists($envPath)) { - $this->error('.env file not is exists!'); - return; - } - if (\Mine\Helper\Str::contains(file_get_contents($envPath), $secretName) === false) { - file_put_contents($envPath, "\n{$secretName}={$value}\n", FILE_APPEND); - } else { - file_put_contents($envPath, preg_replace( - "~{$secretName}\\s*=\\s*[^\n]*~", - "{$secretName}=\"{$value}\"", - file_get_contents($envPath) - )); - } - - $this->info('jwt secret generator successfully:' . $value); - } - - public function getSecretName(): string - { - return Str::upper($this->input->getOption('secret-name')); - } - - public function getEnvPath(): string - { - return BASE_PATH . '/.env'; - } - - public function generator(): string - { - return base64_encode(random_bytes(64)); - } - - protected function configure() - { - $this->setHelp('run "php bin/hyperf.php mine:gen-jwt" create the new jwt secret'); - $this->setDescription('MineAdmin system gen jwt command'); - } - - protected function getOptions(): array - { - return [ - 'secret-name', 'sn', InputOption::VALUE_OPTIONAL, 'The jwt secret name.default:jwt_secret', 'jwt_secret', - ]; - } -} diff --git a/src/SecurityHttp/src/ConfigProvider.php b/src/SecurityHttp/src/ConfigProvider.php deleted file mode 100644 index 0f9905a6..00000000 --- a/src/SecurityHttp/src/ConfigProvider.php +++ /dev/null @@ -1,30 +0,0 @@ - [ - [ - 'id' => 'security-http', - 'description' => 'Security http configure', - 'source' => dirname(__DIR__) . '/publish/security.php', - 'destination' => BASE_PATH . '/config/autoload/security.php', - ], - ], - ]; - } -} diff --git a/src/SecurityHttp/src/Constant/TokenValidConstant.php b/src/SecurityHttp/src/Constant/TokenValidConstant.php deleted file mode 100644 index 5023e1f6..00000000 --- a/src/SecurityHttp/src/Constant/TokenValidConstant.php +++ /dev/null @@ -1,28 +0,0 @@ -security = $this->container->get(Security::class); - } - - public function __get($name) - { - return $this->getAttributes()[$name] ?? null; - } - - public function getEntity(): UserInterface - { - return $this->getSecurity()->getToken()->user($this->scene); - } - - public function getIdentifier(): string - { - return call([$this->getEntity(), __FUNCTION__]); - } - - public function getIdentifierName(): string - { - return call([$this->getEntity(), __FUNCTION__]); - } - - public function getRememberToken(): string - { - return call([$this->getEntity(), __FUNCTION__]); - } - - public function setRememberToken(string $token): void - { - call([$this->getEntity(), __FUNCTION__], func_get_args()); - } - - public function getRememberTokenName(): string - { - return call([$this->getEntity(), __FUNCTION__]); - } - - public function getPassword(): string - { - return call([$this->getEntity(), __FUNCTION__]); - } - - public function setPassword(string $password): void - { - call([$this->getEntity(), __FUNCTION__]); - } - - public function getSecurityBuilder(): Builder - { - return call([$this->getEntity(), __FUNCTION__]); - } - - public function setAttribute(string $key, mixed $value) - { - return call([$this->getEntity(), __FUNCTION__]); - } - - public function getAttributes(): array - { - return call([$this->getEntity(), __FUNCTION__]); - } - - protected function getSecurity(): Security - { - return $this->security; - } -} diff --git a/src/SecurityHttp/src/Exception/JwtConfigException.php b/src/SecurityHttp/src/Exception/JwtConfigException.php deleted file mode 100644 index b680e05a..00000000 --- a/src/SecurityHttp/src/Exception/JwtConfigException.php +++ /dev/null @@ -1,21 +0,0 @@ -claims(); - if ($config['blacklist_enabled']) { - $cacheKey = $this->getCacheKey($claims->get('jti'), $config); - $blacklistGracePeriod = 0; - $expTime = $claims->get(RegisteredClaims::EXPIRATION_TIME); - if (! is_numeric($expTime)) { - $expTime = $expTime->getTimestamp(); - } - $validUntil = Time::now()->addSeconds($blacklistGracePeriod)->getTimestamp(); - $expTime = Time::timestamp($expTime); - $nowTime = Time::now(); - $tokenCacheTime = $expTime->max($nowTime)->diffInSeconds(); - return $this->storageAdd($cacheKey, ['valid_until' => $validUntil], $tokenCacheTime, $config['blacklist_prefix']); - } - return false; - } - - abstract public function storageAdd(string $cacheKey, array $val, int $tokenCacheTime, string $prefix): bool; - - abstract public function storageGet(string $cacheKey, string $prefix): mixed; - - abstract public function storageDelete(string $cacheKey, string $prefix): bool; - - abstract public function storageClear(string $prefix): bool; - - /** - * Determine if the token has been blacklisted. - * @param mixed $claims - */ - public function has($claims, array $config = []): bool - { - $cacheKey = $this->getCacheKey($claims['jti'], $config); - if ($config['blacklist_enabled'] && $config['login_type'] === 'mpop') { - $val = $this->storageGet($cacheKey, $config['blacklist_prefix']); - return ! empty($val['valid_until']) && ! Time::isFuture($val['valid_until']); - } - - if ($config['blacklist_enabled'] && $config['login_type'] === 'sso') { - $val = $this->storageGet($cacheKey, $config['blacklist_prefix']); - // When refreshing the token, the cache time may be the same as the issue time - if (! is_null($claims['iat']) && ! empty($val['valid_until'])) { - $isFuture = ($claims['iat']->getTimestamp() - $val['valid_until']) >= 0; - } else { - $isFuture = false; - } - // check whether the expiry + grace has past - return ! $isFuture; - } - return false; - } - - public function remove($key, array $config = []): void - { - $this->storageDelete($key, $config['blacklist_prefix']); - } - - public function clear(array $config = []): void - { - $this->storageClear($config['blacklist_prefix']); - } - - private function getCacheKey(string $jti, array $config = []): string - { - return sprintf('%s_%s', $config['blacklist_prefix'], $jti); - } -} diff --git a/src/SecurityHttp/src/Jwt/Black/CacheBlack.php b/src/SecurityHttp/src/Jwt/Black/CacheBlack.php deleted file mode 100644 index 4e3e8655..00000000 --- a/src/SecurityHttp/src/Jwt/Black/CacheBlack.php +++ /dev/null @@ -1,42 +0,0 @@ -cache->set($prefix . ':' . $cacheKey, $val, $tokenCacheTime); - } - - public function storageGet(string $cacheKey, string $prefix): mixed - { - return $this->cache->get($prefix . ':' . $cacheKey); - } - - public function storageDelete(string $cacheKey, string $prefix): bool - { - return $this->cache->delete($prefix . ':' . $cacheKey); - } - - public function storageClear(string $prefix): bool - { - return $this->cache->delete($prefix . ':*'); - } -} diff --git a/src/SecurityHttp/src/Jwt/Token.php b/src/SecurityHttp/src/Jwt/Token.php deleted file mode 100644 index 09554a71..00000000 --- a/src/SecurityHttp/src/Jwt/Token.php +++ /dev/null @@ -1,61 +0,0 @@ -getRequest(); - if ($request === null) { - throw new \RuntimeException('Request is not available.'); - } - if (! $request->hasHeader('Authorization')) { - throw new TokenValidException(TokenValidConstant::TOKEN_NOT_FOUND, 'Token is not available.'); - } - $token = str_replace('Bearer ', '', $request->getHeaderLine('Authorization')); - $scene = $param[0] ?? 'default'; - $resolveToken = $this->jwt->parse($token, $scene); - $attributes = $resolveToken->claims()->all(); - $entity = $this->getUserEntity(); - foreach ($attributes as $key => $value) { - $entity->setAttribute(str_replace('__attribute__', '', $key), $value); - } - return $entity; - } - - private function getUserEntity(): UserInterface - { - return new ($this->config->get('entity')); - } - - private function getRequest(): ?RequestInterface - { - return $this->container->get(RequestInterface::class); - } -} diff --git a/src/SecurityHttp/src/RegisterCurrentUserPropertyHandler.php b/src/SecurityHttp/src/RegisterCurrentUserPropertyHandler.php deleted file mode 100644 index 28269219..00000000 --- a/src/SecurityHttp/src/RegisterCurrentUserPropertyHandler.php +++ /dev/null @@ -1,43 +0,0 @@ -setValue(new CurrentUserProxy( - $annotation->secret, - $container, - )); - } - } -} diff --git a/src/SecurityHttp/src/Support/Jwt.php b/src/SecurityHttp/src/Support/Jwt.php deleted file mode 100644 index 548a563b..00000000 --- a/src/SecurityHttp/src/Support/Jwt.php +++ /dev/null @@ -1,258 +0,0 @@ -getSceneConfig($scene); - $loginType = $config['login_type']; - $ssoKey = $config['sso_key']; - $claims = $tokenObject->getClaims(); - $headers = $tokenObject->getHeaders(); - if ($loginType === 'mpop') { // Multi-login, scenario values with a unique id - $unique = uniqid($scene . '_', true); - } else { // 单点登录 - if (empty($claims[$ssoKey])) { - throw new JwtConfigException("There is no {$ssoKey} key in the claims", 400); - } - // unique - $unique = $scene . '_' . $claims[$ssoKey]; - } - - $signer = $this->getSigner($config); - $time = Carbon::now()->addSeconds($this->getTtl($config)); - $datetimeImmutable = $time->toDateTimeImmutable(); - $builder = $this->getConfiguration($signer, $this->getKey($config))->builder(); - $builder = $builder - ->issuedBy($tokenObject->getIssuedBy()) - ->identifiedBy($unique) - ->issuedAt($datetimeImmutable) - ->canOnlyBeUsedAfter($datetimeImmutable) - ->expiresAt($datetimeImmutable); - - $claims['scene'] = $scene; // Add scene values - foreach ($claims as $k => $v) { - $builder = $builder->withClaim($k, $v); // Customized data - } - foreach ($headers as $k => $v) { - $builder = $builder->withHeader($k, $v); // Customized data - } - - $token = $builder->getToken($signer, $this->getKey($config)); // Retrieves the generated token - - // Single sign-on to invalidate all previously generated tokens - if ($loginType === 'sso' && $tokenObject->isInsertSsoBlack) { - $this->getBlack($config)->add($token, $config); - } - - return $token; - } - - public function getTtl(array $config): int - { - return (int) $config['ttl']; - } - - /** - * @throws JwtConfigException - */ - public function parse(string $token, string $scene = 'default', bool $validate = true): Token - { - $config = $this->getSceneConfig($scene); - $signer = new $config['supported_algs'][$config['alg']](); - $parser = $this->getConfiguration($signer, $this->getKey($config))->parser(); - $resolveToken = $parser->parse($token); - $claims = $resolveToken->claims()->all(); - $tokenScene = $claims['scene']; - // Get the scenario configuration for the current environment and verify that the token is generated by that configuration. - if ($tokenScene !== $scene && $this->getIndependentTokenVerify($config)) { - throw new TokenValidException('Token authentication does not pass', 401); - } - // Judging from the configuration information, set the scene. scene corresponding to the current token - if (! $this->getIndependentTokenVerify($config)) { - $scene = $tokenScene ?? $scene; - } - $config = $this->getSceneConfig($scene); - $signer = new $config['supported_algs'][$config['alg']](); - - // Verify that the token is blacklisted - if ($config['blacklist_enabled'] && $this->getBlack($config)->has($claims, $config)) { - throw new TokenValidException(TokenValidConstant::IN_BLACKLIST, 'Token authentication does not pass'); - } - - if ($validate && ! $this->getValidationData($signer, $this->getKey($config), $token)) { - throw new TokenValidException(TokenValidConstant::PARSER_DATA_VALID, 'Token authentication does not pass'); - } - return $resolveToken; - } - - /** - * Refresh token. - * @throws JwtConfigException - */ - public function refreshToken(string $token, string $scene = 'default'): Token - { - $oldToken = $this->parse($token, $scene); - $claims = $oldToken->claims()->all(); - $headers = $oldToken->headers()->all(); - unset( - $claims['iat'], - $claims['nbf'], - $claims['exp'], - $claims['jti'], - $claims['iss'], - ); - $tokenInstance = new TokenObject(); - $tokenInstance->setHeaders($headers); - $tokenInstance->setClaims($claims); - return $this->generator($tokenInstance, $scene); - } - - /** - * Invalidate the token. - * @throws JwtConfigException - */ - public function logout(string $token, string $scene = 'default'): bool - { - $config = $this->getSceneConfig($scene); - return $this->getBlack($config)->add($this->parse($token), $config); - } - - /** - * Get token dynamic validity time. - */ - public function getTokenDynamicCacheTime(string $token): int - { - $claims = $this->parse($token)->claims(); - /** - * @var null|\DateTimeImmutable $exp - */ - if ($exp = $claims->get(RegisteredClaims::EXPIRATION_TIME)) { - return Carbon::now()->diffInSeconds($exp); - } - return -1; - } - - public function getIndependentTokenVerify(array $config): bool - { - return $config['independentTokenVerify'] ?? false; - } - - public function getValidationData(Signer $signer, Key $key, string $token): bool - { - $config = $this->getConfiguration($signer, $key); - $parser = $config->parser()->parse($token); - $claims = $parser->claims()->all(); - - $config->setValidationConstraints(new IdentifiedBy($claims['jti'])); - $config->setValidationConstraints(new SignedWith($signer, $key)); - - if (! $config->validator()->validate($parser, ...$config->validationConstraints())) { - return false; - } - - return true; - } - - public function getConfiguration(Signer $signer, Key $key): Configuration - { - return Configuration::forSymmetricSigner($signer, $key); - } - - public function getBlack(array $config): BlackContract - { - if (empty($config['black']) || class_exists($config['black']) === false) { - throw new JwtConfigException(sprintf('jwt config black is empty')); - } - return $this->container->get($config['black']); - } - - public function getSceneConfig(string $scene): array - { - $config = $this->config->get('jwt', []); - $isDefault = $scene === 'default'; - if (empty($config)) { - throw new JwtConfigException(sprintf('jwt config is empty')); - } - if (! $isDefault && ! isset($config['scene'][$scene])) { - throw new JwtConfigException(sprintf('jwt config scene is empty. %s', $scene)); - } - $sceneConfig = $isDefault ? $config : $config['scene'][$scene]; - if (! $isDefault) { - // If it is not the default scenario, the default configuration is merged in - $keys = [ - 'login_type', 'sso_key', 'secret', 'keys', - 'ttl', 'alg', 'supported_algs', 'symmetry_algs', - 'asymmetric_algs', 'blacklist_enabled', 'blacklist_grace_period', - 'blacklist_cache_ttl', 'blacklist_prefix', 'black', - ]; - foreach ($keys as $key) { - if (empty($sceneConfig[$key])) { - $sceneConfig[$key] = $config[$key]; - } - } - } - return $sceneConfig; - } - - /** - * Get the key needed by the corresponding algorithm. - */ - public function getKey(array $config, string $type = 'private'): ?Key - { - $key = null; - // symmetry algorithm - if (in_array($config['alg'], $config['symmetry_algs'], true)) { - $key = InMemory::base64Encoded($config['secret']); - } - - // asymmetric - if (in_array($config['alg'], $config['asymmetric_algs'], true)) { - $key = InMemory::base64Encoded($config['keys'][$type]); - } - return $key; - } - - public function getSigner(array $config): Signer - { - return new $config['supported_algs'][$config['alg']](); - } -} diff --git a/src/SecurityHttp/src/Support/Time.php b/src/SecurityHttp/src/Support/Time.php deleted file mode 100644 index b84f8734..00000000 --- a/src/SecurityHttp/src/Support/Time.php +++ /dev/null @@ -1,50 +0,0 @@ -timezone('UTC'); - } - - /** - * Checks if a timestamp is in the past. - */ - public static function isPast(int $timestamp, int $leeway = 0): bool - { - return static::timestamp($timestamp)->addSeconds($leeway)->isPast(); - } - - /** - * Checks if a timestamp is in the future. - */ - public static function isFuture(int $timestamp, int $leeway = 0): bool - { - return static::timestamp($timestamp)->subSeconds($leeway)->isFuture(); - } -} diff --git a/src/SecurityHttp/src/TokenObject.php b/src/SecurityHttp/src/TokenObject.php deleted file mode 100644 index 46cbd19f..00000000 --- a/src/SecurityHttp/src/TokenObject.php +++ /dev/null @@ -1,54 +0,0 @@ -issuedBy; - } - - public function setIssuedBy(string $issuedBy): void - { - $this->issuedBy = $issuedBy; - } - - public function getClaims(): array - { - return $this->claims; - } - - public function setClaims(array $claims): void - { - $this->claims = $claims; - } - - public function getHeaders(): array - { - return $this->headers; - } - - public function setHeaders(array $headers): void - { - $this->headers = $headers; - } -} diff --git a/src/SecurityHttp/src/UserProvider.php b/src/SecurityHttp/src/UserProvider.php deleted file mode 100644 index 459c46ad..00000000 --- a/src/SecurityHttp/src/UserProvider.php +++ /dev/null @@ -1,75 +0,0 @@ -generatorToken($entity) : null; - } - - public function retrieveByCredentials(array $credentials): ?object - { - $entity = $this->credentials($credentials); - return $entity ? $this->generatorToken($entity) : null; - } - - private function generatorToken(UserInterface $user): Token - { - $attribute = $user->getAttributes(); - $clams = []; - foreach ($attribute as $key => $value) { - if ($value === $user->getPassword()) { - continue; - } - $clams['__attribute__' . $key] = $value; - } - $tokenInstance = new TokenObject(); - $tokenInstance->setClaims($clams); - $tokenInstance->setIssuedBy($user->getIdentifier()); - return $this->jwt->generator($tokenInstance, self::getScene()); - } -} diff --git a/src/SecurityHttp/tests/Cases/Command/GenJwtSecretCommandTest.php b/src/SecurityHttp/tests/Cases/Command/GenJwtSecretCommandTest.php deleted file mode 100644 index f215a3c8..00000000 --- a/src/SecurityHttp/tests/Cases/Command/GenJwtSecretCommandTest.php +++ /dev/null @@ -1,73 +0,0 @@ -getMethod('generator'); - $this->assertIsString($invoke->invoke(\Mockery::mock(GenJwtSecretCommand::class))); - } - - public function testGetSecretName(): void - { - $reflection = new \ReflectionClass(GenJwtSecretCommand::class); - $invoke = $reflection->getMethod('getSecretName'); - $instance = $reflection->newInstanceWithoutConstructor(); - $input = \Mockery::mock(Input::class); - $input->allows('getOption')->andReturn('xxx'); - $instance->setInput($input); - $this->assertSame('XXX', $invoke->invoke($instance)); - } - - public function testGetEnvPath(): void - { - $reflection = new \ReflectionClass(GenJwtSecretCommand::class); - $invoke = $reflection->getMethod('getEnvPath'); - $m = \Mockery::mock(GenJwtSecretCommand::class); - $this->assertSame(BASE_PATH . '/.env', $invoke->invoke($m)); - } - - public function testInvoke(): void - { - $reflection = new \ReflectionClass(GenJwtSecretCommand::class); - $invoke = $reflection->getMethod('__invoke'); - $m = \Mockery::mock(GenJwtSecretCommand::class); - $m->shouldAllowMockingProtectedMethods(); - $env = sys_get_temp_dir() . '/' . Str::random(32) . '.env'; - file_put_contents($env, "xxx=1\n"); - $m->allows('getEnvPath')->andReturn($env); - $input = \Mockery::mock(Input::class); - $input->allows('getOption')->andReturn('Demo'); - $m->allows('setInput'); - $m->allows('getSecretName')->andReturn('DEMO'); - $m->allows('generator')->andReturn(base64_encode(random_bytes(64))); - $m->allows('info')->andReturnUsing(function ($v) { - echo $v; - }); - $m->setInput($input); - $invoke->invoke($m); - $this->assertTrue(str_contains(file_get_contents($env), 'DEMO')); - } -} diff --git a/src/SecurityHttp/tests/Cases/ConfigProviderTest.php b/src/SecurityHttp/tests/Cases/ConfigProviderTest.php deleted file mode 100644 index 7daf08b1..00000000 --- a/src/SecurityHttp/tests/Cases/ConfigProviderTest.php +++ /dev/null @@ -1,28 +0,0 @@ -assertIsArray((new ConfigProvider())()); - } -} diff --git a/src/SecurityHttp/tests/Cases/Jwt/Black/AbstractBlackTest.php b/src/SecurityHttp/tests/Cases/Jwt/Black/AbstractBlackTest.php deleted file mode 100644 index 0a098877..00000000 --- a/src/SecurityHttp/tests/Cases/Jwt/Black/AbstractBlackTest.php +++ /dev/null @@ -1,119 +0,0 @@ -black = new DummyBlack(); - } - - public function testAdd(): void - { - $token = \Mockery::mock(UnencryptedToken::class); - $config = [ - 'blacklist_enabled' => true, - 'blacklist_prefix' => 'prefix', - ]; - $token->allows('claims')->andReturn(new DataSet([ - 'xxx' => 'xxx', - 'jti' => 'xxx', - RegisteredClaims::EXPIRATION_TIME => Carbon::now()->toDateTimeImmutable(), - ], RegisteredClaims::class)); - - $this->assertTrue($this->black->add($token, $config)); - } - - public function testAddDisable() - { - $token = \Mockery::mock(UnencryptedToken::class); - $config = [ - 'blacklist_enabled' => false, - 'blacklist_prefix' => 'prefix', - ]; - $token->allows('claims')->andReturn(new DataSet([ - 'xxx' => 'xxx', - 'jti' => 'xxx', - RegisteredClaims::EXPIRATION_TIME => Carbon::now()->toDateTimeImmutable(), - ], RegisteredClaims::class)); - $this->assertFalse($this->black->add($token, $config)); - } - - public function testHasWithMpopLoginType(): void - { - $claims = [ - 'jti' => '123456', - 'iat' => Time::now(), - ]; - - $config = [ - 'blacklist_enabled' => true, - 'login_type' => 'mpop', - 'blacklist_prefix' => 'prefix', - ]; - $this->assertFalse($this->black->has($claims, $config)); - } - - public function testHasWithSsoLoginType(): void - { - $claims = [ - 'jti' => '123456', - 'iat' => Time::now(), - ]; - - $config = [ - 'blacklist_enabled' => true, - 'login_type' => 'sso', - 'blacklist_prefix' => 'prefix', - ]; - - $this->assertTrue($this->black->has($claims, $config)); - } - - public function testRemove(): void - { - $key = '123456'; - $config = [ - 'blacklist_prefix' => 'prefix', - ]; - - $this->black->remove($key, $config); - $this->assertTrue(true); - } - - public function testClear(): void - { - $config = [ - 'blacklist_prefix' => 'prefix', - ]; - - $this->black->clear($config); - $this->assertTrue(true); - } -} diff --git a/src/SecurityHttp/tests/Cases/Jwt/Black/CacheBlackTest.php b/src/SecurityHttp/tests/Cases/Jwt/Black/CacheBlackTest.php deleted file mode 100644 index 62f5820f..00000000 --- a/src/SecurityHttp/tests/Cases/Jwt/Black/CacheBlackTest.php +++ /dev/null @@ -1,76 +0,0 @@ -cacheProphecy = \Mockery::mock(CacheInterface::class); - $this->cacheBlack = new CacheBlack($this->cacheProphecy); - } - - public function testStorageAdd(): void - { - $cacheKey = 'testKey'; - $val = ['testVal']; - $tokenCacheTime = 3600; - $prefix = 'testPrefix'; - - $this->cacheProphecy->allows('set') - ->with($prefix . ':' . $cacheKey, $val, $tokenCacheTime) - ->andReturn(true); - - $result = $this->cacheBlack->storageAdd($cacheKey, $val, $tokenCacheTime, $prefix); - $this->assertTrue($result); - } - - public function testStorageGet(): void - { - $cacheKey = 'testKey'; - $val = ['testVal']; - $prefix = 'testPrefix'; - $this->cacheProphecy->allows('get')->andReturn($prefix . ':' . $cacheKey)->andReturn($val); - $result = $this->cacheBlack->storageGet($cacheKey, $prefix); - $this->assertSame($val, $result); - } - - public function testStorageDelete(): void - { - $cacheKey = 'testKey'; - $prefix = 'testPrefix'; - $this->cacheProphecy->allows('delete')->with($prefix . ':' . $cacheKey)->andReturn(true); - $result = $this->cacheBlack->storageDelete($cacheKey, $prefix); - $this->assertTrue($result); - } - - public function testStorageClear(): void - { - $prefix = 'testPrefix'; - $this->cacheProphecy->allows('delete')->with($prefix . ':*')->andReturn(true); - $result = $this->cacheBlack->storageClear($prefix); - $this->assertTrue($result); - } -} diff --git a/src/SecurityHttp/tests/Cases/Jwt/TokenTest.php b/src/SecurityHttp/tests/Cases/Jwt/TokenTest.php deleted file mode 100644 index 6f01b02e..00000000 --- a/src/SecurityHttp/tests/Cases/Jwt/TokenTest.php +++ /dev/null @@ -1,140 +0,0 @@ -jwt = \Mockery::mock(Jwt::class); - $this->config = \Mockery::mock(Config::class); - $this->container = \Mockery::mock(ContainerInterface::class); - $this->token = new Token($this->jwt, $this->config, $this->container); - $this->request = \Mockery::mock(RequestInterface::class); - } - - public function testUserWithoutRequest(): void - { - $this->container->allows('get') - ->with(RequestInterface::class) - ->andReturnNull(); - - $this->expectException(\RuntimeException::class); - $this->expectExceptionMessage('Request is not available.'); - $this->token->user(); - } - - public function testUserWithoutAuthorizationHeader(): void - { - $this->container->allows('get') - ->with(RequestInterface::class) - ->once() - ->andReturn($this->request); - - $this->request->allows('hasHeader') - ->with('Authorization') - ->once() - ->andReturnFalse(); - - $this->expectException(TokenValidException::class); - $this->expectExceptionMessage('Token is not available.'); - $this->token->user(); - } - - public function testUserWithValidToken(): void - { - $token = 'Bearer test.token'; - $scene = 'default'; - $attributes = [ - 'username' => 'test_user', - 'email' => 'test@example.com', - ]; - - $this->container->allows('get') - ->with(RequestInterface::class) - ->andReturns($this->request); - - $this->request->allows('hasHeader') - ->with('Authorization') - ->andReturnTrue(); - - $this->request->allows('getHeaderLine') - ->with('Authorization') - ->andReturns($token); - - $resolveToken = \Mockery::mock(UnencryptedToken::class); - $resolveToken->allows('claims') - ->once() - ->andReturn(new DataSet($attributes, 'xxx')); - - $this->jwt->allows('parse') - ->with('test.token', $scene) - ->once() - ->andReturn($resolveToken); - - $this->config->allows('get') - ->with('entity') - ->once() - ->andReturn(UserModel::class); - - $user = $this->token->user(); - - $this->assertInstanceOf(UserInterface::class, $user); - foreach ($attributes as $key => $value) { - $this->assertEquals($value, $user->getAttribute(str_replace('__attribute__', '', $key))); - } - } -} diff --git a/src/SecurityHttp/tests/Cases/Support/JwtTest.php b/src/SecurityHttp/tests/Cases/Support/JwtTest.php deleted file mode 100644 index 8cd6bfc3..00000000 --- a/src/SecurityHttp/tests/Cases/Support/JwtTest.php +++ /dev/null @@ -1,266 +0,0 @@ -container = \Mockery::mock(ContainerInterface::class); - $this->jwt = new Jwt( - $this->config = \Mockery::mock(Config::class), - $this->container - ); - $stubConfig = require dirname(__DIR__, 3) . '/publish/security.php'; - $jwtConfig = $stubConfig['jwt']; - $jwtConfig['secret'] = base64_encode(random_bytes(32)); - $jwtConfig['black'] = CacheBlack::class; - $jwtConfig['scene']['sso'] = [ - 'secret' => base64_encode(random_bytes(32)), - 'login_type' => 'sso', - ]; - $this->config->allows('get') - ->with('jwt', []) - ->andReturn($jwtConfig); - } - - public function testGenerator(): void - { - $tokenObject = new TokenObject(); - $tokenObject->setIssuedBy('xxxx'); - $tokenObject->setClaims(['foo' => 'bar']); - $token = $this->jwt->generator($tokenObject); - $this->assertInstanceOf(UnencryptedToken::class, $token); - } - - public function testGeneratorWithSsoKey(): void - { - $black = \Mockery::mock(BlackContract::class); - $config = $this->jwt->getSceneConfig('sso'); - $signer = $this->jwt->getSigner($config); - $key = $this->jwt->getKey($config); - $this->container->allows('get')->with(CacheBlack::class)->andReturn($black); - - $black->allows('add')->andReturn(true); - $tokenObject = new TokenObject(); - $tokenObject->setClaims(['sso_key' => 'foo', 'foo' => 'bar', 'uid' => 111]); - $token = $this->jwt->generator($tokenObject, 'sso'); - $this->assertInstanceOf(UnencryptedToken::class, $token); - $this->assertTrue($this->jwt->getValidationData($signer, $key, $token->toString())); - $tokenObject = new TokenObject(); - $tokenObject->setClaims(['sso_key' => 'foo', 'foo' => 'bar', 'uid' => 111]); - $newToken = $this->jwt->generator($tokenObject, 'sso'); - $black->allows('has')->andReturnUsing(function ($key, $data) { - return $key['jti'] === 'sso_111'; - }); - try { - $this->jwt->parse($token->toString(), 'sso', true); - } catch (\Exception $e) { - $this->assertEquals($e->getMessage(), 'Token authentication does not pass'); - } - $this->expectException(JwtConfigException::class); - $tokenObject->setClaims(['sso_key' => 'foo', 'foo' => 'bar']); - $this->jwt->generator($tokenObject, 'sso'); - } - - public function testGeneratorWithUnique(): void - { - $tokenObject = new TokenObject(); - $tokenObject->setClaims(['foo' => 'bar']); - $token = $this->jwt->generator($tokenObject); - $this->assertInstanceOf(UnencryptedToken::class, $token); - } - - public function testParse(): void - { - $black = \Mockery::mock(BlackContract::class); - $this->container->allows('get')->andReturn(CacheBlack::class)->andReturn($black); - $black->allows('has')->andReturn(false); - $object = new TokenObject(); - $object->setIssuedBy('xxxx'); - $object->setClaims(['foo' => 'bar', 'uid' => 'xxx']); - $token = $this->jwt->generator($object); - $parsedToken = $this->jwt->parse($token->toString()); - $this->assertInstanceOf(Token::class, $parsedToken); - } - - public function testParseWithSsoKey(): void - { - $black = \Mockery::mock(BlackContract::class); - $this->container->allows('get')->andReturn(CacheBlack::class)->andReturn($black); - $black->allows('has')->andReturn(false); - $black->allows('add')->andReturn(true); - $tokenObject = new TokenObject(); - $tokenObject->setClaims(['sso_key' => 'foo', 'foo' => 'bar', 'uid' => 'xxx']); - $token = $this->jwt->generator($tokenObject, 'sso'); - $parsedToken = $this->jwt->parse($token->toString(), 'sso'); - $this->assertInstanceOf(Token::class, $parsedToken); - } - - public function testParseWithIndependentTokenVerify(): void - { - $black = \Mockery::mock(BlackContract::class); - $this->container->allows('get')->andReturn(CacheBlack::class)->andReturn($black); - $black->allows('has')->andReturn(false); - $black->allows('add')->andReturn(true); - $tokenObject = new TokenObject(); - $tokenObject->setClaims(['sso_key' => 'foo', 'foo' => 'bar', 'uid' => 'xxx']); - $token = $this->jwt->generator($tokenObject, 'sso'); - $jwt = new Jwt($this->config, $this->container); - $parsedToken = $jwt->parse($token->toString()); - $this->assertInstanceOf(Token::class, $parsedToken); - } - - public function testParseWithValidate(): void - { - $black = \Mockery::mock(BlackContract::class); - $this->container->allows('get')->andReturn(CacheBlack::class)->andReturn($black); - $black->allows('has')->andReturn(false); - $black->allows('add')->andReturn(true); - $tokenObject = new TokenObject(); - $tokenObject->setClaims(['sso_key' => 'foo', 'foo' => 'bar', 'uid' => 'xxx']); - $token = $this->jwt->generator($tokenObject); - $parsedToken = $this->jwt->parse($token->toString(), 'default', false); - $this->assertInstanceOf(Token::class, $parsedToken); - } - - public function testRefreshToken() - { - $black = \Mockery::mock(BlackContract::class); - $this->container->allows('get')->andReturn(CacheBlack::class)->andReturn($black); - $black->allows('has')->andReturn(false); - $black->allows('add')->andReturn(true); - $tokenObject = new TokenObject(); - $tokenObject->setClaims(['sso_key' => 'foo', 'foo' => 'bar', 'uid' => 'xxx']); - $token = $this->jwt->generator($tokenObject); - $refreshedToken = $this->jwt->refreshToken($token->toString()); - $this->assertInstanceOf(Token::class, $refreshedToken); - } - - public function testLogout() - { - $black = \Mockery::mock(CacheBlack::class); - $this->container->allows('get')->with(CacheBlack::class)->andReturn($black); - $instance = new TokenObject(); - $instance->setIssuedBy('xxxxx'); - $instance->setClaims([ - 'name' => 'zds', - ]); - $token = $this->jwt->generator($instance); - - $black->allows('has')->andReturn(false, true); - $black->allows('add')->andReturn(true); - $this->assertTrue($this->jwt->logout($token->toString())); - $this->expectException(TokenValidException::class); - $this->assertTrue($this->jwt->logout($token->toString())); - } - - public function testGetTokenDynamicCacheTime(): void - { - $black = \Mockery::mock(CacheBlack::class); - $black->allows('has')->andReturn(false); - $this->container->allows('get')->with(CacheBlack::class)->andReturn($black); - $instance = new TokenObject(); - $instance->setIssuedBy('xxxxx'); - $instance->setClaims([ - 'name' => 'zds', - ]); - $token = $this->jwt->generator($instance); - $dynamicCacheTime = $this->jwt->getTokenDynamicCacheTime($token->toString()); - $this->assertIsInt($dynamicCacheTime); - $this->assertEquals(7199, $dynamicCacheTime); - sleep(1); - $this->assertEquals(7198, $this->jwt->getTokenDynamicCacheTime($token->toString())); - } - - public function testGetIndependentTokenVerify(): void - { - $config = ['independentTokenVerify' => true]; - $this->assertTrue($this->jwt->getIndependentTokenVerify($config)); - } - - public function testGetValidationData(): void - { - $config = $this->jwt->getSceneConfig('default'); - $black = \Mockery::mock(CacheBlack::class); - $black->allows('has')->andReturn(false); - $this->container->allows('get')->with(CacheBlack::class)->andReturn($black); - $instance = new TokenObject(); - $instance->setIssuedBy('xxxxx'); - $instance->setClaims([ - 'name' => 'zds', - ]); - $token = $this->jwt->generator($instance); - $signer = $this->jwt->getSigner($config); - $key = $this->jwt->getKey($config); - $this->assertTrue($this->jwt->getValidationData($signer, $key, $token->toString())); - } - - /** - * @throws JwtConfigException - */ - public function testGetConfiguration(): void - { - $config = $this->jwt->getSceneConfig('default'); - $signer = $this->jwt->getSigner($config); - $key = $this->jwt->getKey($config); - $config = $this->jwt->getConfiguration($signer, $key); - $this->assertInstanceOf(Configuration::class, $config); - } - - public function testGetBlack(): void - { - $config = ['black' => CacheBlack::class]; - $this->container->allows('get') - ->andReturn(CacheBlack::class) - ->andReturn(\Mockery::mock(CacheBlack::class)); - $black = $this->jwt->getBlack($config); - $this->assertInstanceOf(BlackContract::class, $black); - } - - public function testGetBlackWithInvalidClass(): void - { - $config = ['black' => 'InvalidClass']; - $this->expectException(JwtConfigException::class); - $this->jwt->getBlack($config); - } - - public function testGetSceneConfig(): void - { - $sceneConfig = $this->jwt->getSceneConfig('default'); - $this->assertIsArray($sceneConfig); - } -} diff --git a/src/SecurityHttp/tests/Cases/Support/TimeTest.php b/src/SecurityHttp/tests/Cases/Support/TimeTest.php deleted file mode 100644 index 61b954bf..00000000 --- a/src/SecurityHttp/tests/Cases/Support/TimeTest.php +++ /dev/null @@ -1,92 +0,0 @@ -assertInstanceOf(Carbon::class, $time); - $this->assertSame('UTC', $time->getTimezone()->getName()); - } - - public function testTimestampReturnsCarbonInstance(): void - { - $timestamp = 1630000000; - $time = Time::timestamp($timestamp); - - $this->assertInstanceOf(Carbon::class, $time); - $this->assertSame('UTC', $time->getTimezone()->getName()); - $this->assertSame($timestamp, $time->getTimestamp()); - } - - public function testIsPastReturnsTrueForPastTimestamp(): void - { - $timestamp = time() - 60; // 60 seconds ago - $result = Time::isPast($timestamp); - - $this->assertTrue($result); - } - - public function testIsPastReturnsFalseForFutureTimestamp(): void - { - $timestamp = time() + 60; // 60 seconds later - $result = Time::isPast($timestamp); - - $this->assertFalse($result); - } - - public function testIsPastReturnsFalseForCurrentTimestampWithLeeway(): void - { - $timestamp = time(); - $leeway = 60; // 60 seconds - $result = Time::isPast($timestamp, $leeway); - - $this->assertFalse($result); - } - - public function testIsFutureReturnsTrueForFutureTimestamp(): void - { - $timestamp = time() + 60; // 60 seconds later - $result = Time::isFuture($timestamp); - - $this->assertTrue($result); - } - - public function testIsFutureReturnsFalseForPastTimestamp(): void - { - $timestamp = time() - 60; // 60 seconds ago - $result = Time::isFuture($timestamp); - - $this->assertFalse($result); - } - - public function testIsFutureReturnsFalseForCurrentTimestampWithLeeway(): void - { - $timestamp = time(); - $leeway = 60; // 60 seconds - $result = Time::isFuture($timestamp, $leeway); - - $this->assertFalse($result); - } -} diff --git a/src/SecurityHttp/tests/Cases/TokenObjectTest.php b/src/SecurityHttp/tests/Cases/TokenObjectTest.php deleted file mode 100644 index c5df1cae..00000000 --- a/src/SecurityHttp/tests/Cases/TokenObjectTest.php +++ /dev/null @@ -1,36 +0,0 @@ -assertTrue(true); - $instance = new TokenObject(); - $this->assertInstanceOf(TokenObject::class, $instance); - $instance->setIssuedBy('xxx'); - $this->assertEquals('xxx', $instance->getIssuedBy()); - $instance->setClaims(['xxxx']); - $this->assertEquals(['xxxx'], $instance->getClaims()); - $instance->setHeaders(['xxxx']); - $this->assertEquals(['xxxx'], $instance->getHeaders()); - } -} diff --git a/src/SecurityHttp/tests/Cases/UserProviderTest.php b/src/SecurityHttp/tests/Cases/UserProviderTest.php deleted file mode 100644 index 4721f99b..00000000 --- a/src/SecurityHttp/tests/Cases/UserProviderTest.php +++ /dev/null @@ -1,139 +0,0 @@ -assertEquals('admin', UserProvider::getScene()); - } - - public function testRetrieveByCredentials(): void - { - $event = \Mockery::mock(EventDispatcherInterface::class); - $config = \Mockery::mock(Config::class); - $config->allows('get') - ->with('entity', '\App\Model\User') - ->andReturn(UserModel::class); - $builder = \Mockery::mock(Builder::class); - ApplicationContext::getContainer()->set('mock.builder', $builder); - $jwt = \Mockery::mock(Jwt::class); - $userProvider = new UserProvider($event, $config, $jwt); - $this->assertInstanceOf(UserProvider::class, $userProvider); - $this->assertNull($userProvider->retrieveByCredentials([ - 'username' => 'admin@qq.com', - 'password' => '123456', - ])); - $builder->allows('where')->andReturnUsing(function ($column, $value) use ($builder) { - if ($column === 'email') { - $this->assertEquals('xxx@qq.com', $value); - } - return $builder; - }); - $user = new UserModel(); - $user->setPassword(password_hash('123456', PASSWORD_DEFAULT)); - $user2 = clone $user; - $user2->setPassword(password_hash('1234567', PASSWORD_DEFAULT)); - $builder->allows('first')->andReturn($user, $user2); - $jwt->allows('generator')->andReturnUsing(function (TokenObject $token) { - $this->assertEquals('xxx@qq.com', $token->getIssuedBy()); - $this->assertEquals([ - '__attribute__id' => 1, - '__attribute__email' => 'xxx@qq.com', - ], $token->getClaims()); - return \Mockery::mock(UnencryptedToken::class); - }); - $event->allows('dispatch')->andReturnUsing(function ($event) { - $this->assertInstanceOf(Verified::class, $event); - }, function ($event) { - $this->assertInstanceOf(Login::class, $event); - }, function ($event) { - $this->assertInstanceOf(Validated::class, $event); - }); - $this->assertInstanceOf(UnencryptedToken::class, $userProvider->retrieveByCredentials([ - 'email' => 'xxx@qq.com', - 'password' => '123456', - ])); - $this->assertNull($userProvider->retrieveByCredentials([ - 'email' => 'xxx@qq.com', - 'password' => '123456', - ])); - } - - public function testRetrieveById() - { - $event = \Mockery::mock(EventDispatcherInterface::class); - $config = \Mockery::mock(Config::class); - $config->allows('get') - ->with('entity', '\App\Model\User') - ->andReturn(UserModel::class); - $builder = \Mockery::mock(Builder::class); - ApplicationContext::getContainer()->set('mock.builder', $builder); - $jwt = \Mockery::mock(Jwt::class); - $userProvider = new UserProvider($event, $config, $jwt); - $this->assertInstanceOf(UserProvider::class, $userProvider); - - $user = new UserModel(); - $user2 = clone $user; - $builder->allows('first')->andReturn(null, $user, $user2); - $builder->allows('where')->andReturn($builder); - $jwt->allows('generator')->andReturnUsing(function (TokenObject $token) { - $this->assertEquals('xxx@qq.com', $token->getIssuedBy()); - $this->assertEquals([ - '__attribute__id' => 1, - '__attribute__email' => 'xxx@qq.com', - ], $token->getClaims()); - return \Mockery::mock(UnencryptedToken::class); - }); - $this->assertNull($userProvider->retrieveById('123@qq.com')); - $this->assertInstanceOf(UnencryptedToken::class, $userProvider->retrieveById('123@qq.com')); - } - - public function testUpdateRememberToken() - { - $reflection = new \ReflectionClass(UserProvider::class); - $method = $reflection->getMethod('updateRememberToken'); - $instance = \Mockery::mock(UserProvider::class); - try { - $method->invokeArgs($instance, [\Mockery::mock(UserInterface::class), '133213123']); - } catch (\Exception $e) { - $this->assertEquals('Method not implemented', $e->getMessage()); - } - } -} diff --git a/src/SecurityHttp/tests/Stub/DummyBlack.php b/src/SecurityHttp/tests/Stub/DummyBlack.php deleted file mode 100644 index 64e44e87..00000000 --- a/src/SecurityHttp/tests/Stub/DummyBlack.php +++ /dev/null @@ -1,42 +0,0 @@ - 1, - 'email' => 'xxx@qq.com', - 'password' => '', - ]; - - public function getIdentifier(): string - { - return $this->attributes['email']; - } - - public function getIdentifierName(): string - { - return 'email'; - } - - public function getRememberToken(): string - { - return ''; - } - - public function setRememberToken(string $token): void {} - - public function getRememberTokenName(): string - { - return ''; - } - - public function getPassword(): string - { - return $this->attributes['password']; - } - - public function setPassword(string $password): void - { - $this->attributes['password'] = $password; - } - - public function getSecurityBuilder(): Builder - { - return ApplicationContext::getContainer()->get('mock.builder'); - } -} diff --git a/src/translatable/.github/workflows/release.yml b/src/translatable/.github/workflows/release.yml index 0f7d23fa..4ebb5d0a 100644 --- a/src/translatable/.github/workflows/release.yml +++ b/src/translatable/.github/workflows/release.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Create Release id: create_release uses: actions/create-release@v1