Skip to content

Commit

Permalink
add token expire clean up (#125)
Browse files Browse the repository at this point in the history
* add token expire clean up
* improve expired sso identities removal
  • Loading branch information
solverat authored Feb 25, 2020
1 parent fb781a3 commit 26e1ce1
Show file tree
Hide file tree
Showing 9 changed files with 201 additions and 1 deletion.
6 changes: 5 additions & 1 deletion docs/SSO/10_Overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,8 @@ The login type comes with two sub workflows: "*complete_profile*" and "*instant*
Read more about the integration types [here](./11_IntegrationTypes.md)

## Downsides
Currently, there is no oAuth1 Support.
Currently, there is no oAuth1 Support.

## Clean expired identities
By default, no identities get deleted after its expiration.
However, you may want to enable the [clean up listener](./31_Listener.md).
45 changes: 45 additions & 0 deletions docs/SSO/31_Listener.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Listeners

## Clean Up expired Identities
There two ways to clean up expired identities.

> **Note:** This feature is not enabled by default!
### Important Notes
This Clean-Up task also removes the related user within some exceptions:
- The user will **not be deleted** if the given user has other SSO identities
- The user will **not be deleted** if a password has been defined (not empty rule)

### I. Expired Date
Use the `clean_up_expired_tokens` configuration flag to enable the clean-up.
In this example, all identities, older than `expiresAt` will be removed.


```yaml
members:
oauth:
clean_up_expired_tokens: true
```
SQL Explanation:
```mysql
SELECT entites WHERE expiresAt IS NOT NULL AND expiresAt < NOW();
```

### II. Expired By TTL
Use the `clean_up_expired_tokens` configuration flag to enable the clean-up. Use the `expired_tokens_ttl` configuration flag to define a ttl in seconds.
In this example, all identities, older than 24h will be removed.

```yaml
members:
oauth:
clean_up_expired_tokens: true
expired_tokens_ttl: 86400
```
SQL Explanation:
```mysql
SELECT entites WHERE o_creationDate < (UNIX_TIMESTAMP() - $TTL);
```
2 changes: 2 additions & 0 deletions src/MembersBundle/DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ public function getConfigTreeBuilder()
->values(['complete_profile', 'instant'])
->defaultValue('complete_profile')
->end()
->booleanNode('clean_up_expired_tokens')->defaultFalse()->end()
->integerNode('expired_tokens_ttl')->defaultValue(0)->min(0)->end()
->arrayNode('scopes')
->useAttributeAsKey('client')
->prototype('array')
Expand Down
3 changes: 3 additions & 0 deletions src/MembersBundle/DependencyInjection/MembersExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,10 @@ public function load(array $configs, ContainerBuilder $container)
$container->setParameter('members.registration.event.oauth_type', $config['post_register_type_oauth']);
$container->setParameter('members.resetting.retry_ttl', $config['relations']['resetting']['retry_ttl']);
$container->setParameter('members.resetting.token_ttl', $config['relations']['resetting']['token_ttl']);

$container->setParameter('members.oauth.enabled', $config['oauth']['enabled']);
$container->setParameter('members.oauth.clean_up_expired_tokens', $config['oauth']['clean_up_expired_tokens']);
$container->setParameter('members.oauth.expired_tokens_ttl', $config['oauth']['expired_tokens_ttl']);

foreach ($config['relations']['login']['form'] as $confName => $confValue) {
$container->setParameter('members_user.login.form.' . $confName, $confValue);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
<?php

namespace MembersBundle\EventListener\Maintenance;

use MembersBundle\Adapter\User\UserInterface;
use MembersBundle\Manager\SsoIdentityManagerInterface;
use Pimcore\Maintenance\TaskInterface;
use Pimcore\Model\DataObject\Concrete;

class SsoCleanUpExpiredTokenListener implements TaskInterface
{
/**
* @var bool
*/
protected $oauthEnabled;

/**
* @var bool
*/
protected $cleanUpExpiredTokens;

/**
* @var int
*/
protected $expiredTokensTtl;

/**
* @var SsoIdentityManagerInterface
*/
protected $ssoIdentityManager;

/**
* @param bool $oauthEnabled
* @param bool $cleanUpExpiredTokens
* @param int $expiredTokensTtl
* @param SsoIdentityManagerInterface $ssoIdentityManager
*/
public function __construct(
bool $oauthEnabled,
bool $cleanUpExpiredTokens,
int $expiredTokensTtl,
SsoIdentityManagerInterface $ssoIdentityManager
) {
$this->oauthEnabled = $oauthEnabled;
$this->cleanUpExpiredTokens = $cleanUpExpiredTokens;
$this->expiredTokensTtl = $expiredTokensTtl;
$this->ssoIdentityManager = $ssoIdentityManager;
}

/**
* @throws \Exception
*/
public function execute()
{
if ($this->oauthEnabled === false) {
return;
}

if ($this->cleanUpExpiredTokens === false) {
return;
}

$identities = $this->ssoIdentityManager->findExpiredSsoIdentities($this->expiredTokensTtl);

foreach ($identities as $ssoIdentity) {

if (!$ssoIdentity instanceof Concrete) {
continue;
}

$this->handleIdentityRemoval($ssoIdentity);
}
}

/**
* @param Concrete $ssoIdentity
*
* @throws \Exception
*/
protected function handleIdentityRemoval(Concrete $ssoIdentity)
{
$user = $this->ssoIdentityManager->getUserBySsoIdentity($ssoIdentity->getProvider(), $ssoIdentity->getIdentifier());

$ssoIdentity->delete();

if (!$user instanceof UserInterface) {
return;
}

// don't touch a user with a stored password
if (!empty($user->getPassword())) {
return;
}

// don't touch a user if he has other identities
$userSsoIdentities = $this->ssoIdentityManager->getSsoIdentities($user);
if (is_array($userSsoIdentities) && count($userSsoIdentities) > 0) {
return;
}

if (!$user instanceof Concrete) {
return;
}

$user->delete();
}
}
17 changes: 17 additions & 0 deletions src/MembersBundle/Manager/SsoIdentityManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,23 @@ public function saveIdentity(SsoIdentityInterface $ssoIdentity)
$ssoIdentity->save();
}

/**
* {@inheritdoc}
*/
public function findExpiredSsoIdentities(int $ttl = 0)
{
$ssoIdentityListing = $this->classManager->getSsoIdentityListing();

if ($ttl === 0) {
$ssoIdentityListing->addConditionParam('expiresAt IS NOT NULL AND expiresAt < ?', time());
} else {
$query = sprintf('o_creationDate < (UNIX_TIMESTAMP() - %s)', $ttl);
$ssoIdentityListing->addConditionParam($query);
}

return $ssoIdentityListing->getObjects();
}

/**
* @param string $provider
* @param string $identifier
Expand Down
7 changes: 7 additions & 0 deletions src/MembersBundle/Manager/SsoIdentityManagerInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,11 @@ public function createSsoIdentity(UserInterface $user, $provider, $identifier, $
* @throws \Exception
*/
public function saveIdentity(SsoIdentityInterface $ssoIdentity);

/**
* @param int $ttl (seconds)
*
* @return SsoIdentityInterface[]
*/
public function findExpiredSsoIdentities(int $ttl = 0);
}
12 changes: 12 additions & 0 deletions src/MembersBundle/Resources/config/services/oauth/mainentance.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
services:
_defaults:
autowire: true
autoconfigure: true

MembersBundle\EventListener\Maintenance\SsoCleanUpExpiredTokenListener:
arguments:
$oauthEnabled: '%members.oauth.enabled%'
$cleanUpExpiredTokens: '%members.oauth.clean_up_expired_tokens%'
$expiredTokensTtl: '%members.oauth.expired_tokens_ttl%'
tags:
- {name: pimcore.maintenance.task, type: members_sso_clean_up_expired_tokens }
3 changes: 3 additions & 0 deletions src/MembersBundle/Security/OAuth/OAuthRegistrationHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,12 @@ public function getUserFromUserResponse(OAuthResponseInterface $OAuthResponse)
*/
public function connectNewUserWithSsoIdentity(OAuthResponseInterface $oAuthResponse)
{
// @todo: check if user exists? (#122)

/** @var UserInterface $user */
$user = $this->userManager->createUser();

// @todo: improve with #121
$user->setEmail($oAuthResponse->getResourceOwner()->getId());
$user->setPublished(true);

Expand Down

0 comments on commit 26e1ce1

Please sign in to comment.