Skip to content
This repository was archived by the owner on Sep 19, 2022. It is now read-only.

Commit 373d3a3

Browse files
committed
feat: PerunEnsureMember
1 parent 48fd82c commit 373d3a3

File tree

4 files changed

+234
-0
lines changed

4 files changed

+234
-0
lines changed
Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
<?php
2+
3+
namespace SimpleSAML\Module\perun\Auth\Process;
4+
5+
use SimpleSAML\Auth\ProcessingFilter;
6+
use SimpleSAML\Auth\State;
7+
use SimpleSAML\Configuration;
8+
use SimpleSAML\Error\Exception;
9+
use SimpleSAML\Logger;
10+
use SimpleSAML\Module;
11+
use SimpleSAML\Module\perun\Adapter;
12+
use SimpleSAML\Module\perun\model\Member;
13+
use SimpleSAML\Module\perun\PerunConstants;
14+
use SimpleSAML\Utils\HTTP;
15+
16+
class PerunEnsureMember extends ProcessingFilter
17+
{
18+
const LOG_PREFIX = 'perun:PerunEnsureMember: ';
19+
20+
const REGISTER_URL = 'registerUrl';
21+
const VO_SHORT_NAME = 'voShortName';
22+
const GROUP_NAME = 'groupName';
23+
const INTERFACE_PROPNAME = 'interface';
24+
const CALLBACK_PARAMETER_NAME = 'callbackParameterName';
25+
const RPC = 'rpc';
26+
const GROUP = 'group';
27+
28+
const CALLBACK = 'perun/perun_ensure_member_callback.php';
29+
const REDIRECT = 'perun/perun_ensure_member.php';
30+
31+
public const STAGE = 'perun:PerunEnsureMember';
32+
public const PARAM_STATE_ID = PerunConstants::STATE_ID;
33+
public const PARAM_REGISTRATION_URL = 'registrationUrl';
34+
public const TEMPLATE = 'perun:perun-ensure-member-tpl.php';
35+
36+
private $config;
37+
private $filterConfig;
38+
private $registerUrl;
39+
private $voShortName;
40+
private $groupName;
41+
private $callbackParameterName;
42+
private $adapter;
43+
private $rpcAdapter;
44+
45+
public function __construct($config, $reserved)
46+
{
47+
parent::__construct($config, $reserved);
48+
$this->config = $config;
49+
$this->filterConfig = Configuration::loadFromArray($config);
50+
51+
$this->registerUrl = $this->filterConfig->getString(self::REGISTER_URL, '');
52+
if (empty($this->registerUrl)) {
53+
throw new Exception(self::LOG_PREFIX . 'Missing configuration option \'' . self::REGISTER_URL . '\'');
54+
}
55+
56+
$this->voShortName = $this->filterConfig->getString(self::VO_SHORT_NAME, '');
57+
if (empty($this->voShortName)) {
58+
throw new Exception(self::LOG_PREFIX . 'Missing configuration option \'' . self::VO_SHORT_NAME . '\'');
59+
}
60+
61+
$this->callbackParameterName = $this->filterConfig->getString(self::CALLBACK_PARAMETER_NAME, '');
62+
if (empty($this->callbackParameterName)) {
63+
throw new Exception(self::LOG_PREFIX . 'Missing configuration option \'' . self::CALLBACK_PARAMETER_NAME . '\'');
64+
}
65+
66+
$this->groupName = $this->filterConfig->getString(self::GROUP_NAME, '');
67+
68+
$interface = $this->filterConfig->getString(self::INTERFACE_PROPNAME, self::RPC);
69+
$this->adapter = Adapter::getInstance($interface);
70+
71+
$this->rpcAdapter = Adapter::getInstance(self::RPC);
72+
}
73+
74+
public function process(&$request)
75+
{
76+
if (isset($request['perun']['user'])) {
77+
$user = $request['perun']['user'];
78+
} else {
79+
throw new Exception(
80+
self::LOG_PREFIX . 'Missing mandatory field \'perun.user\' in request.' . 'Hint: Did you configured PerunIdentity filter before this filter?'
81+
);
82+
}
83+
84+
$vo = $this->adapter->getVoByShortName($this->voShortName);
85+
if (null === $vo) {
86+
throw new Exception(self::LOG_PREFIX . 'VO with voShortName \'' . self::VO_SHORT_NAME . '\' not found.');
87+
}
88+
89+
$this->handleUser($user, $vo, $request);
90+
}
91+
92+
private function handleUser($user, $vo, $request): void
93+
{
94+
// In this case, we can deal with empty groupName in the same way as with the user which is in the group
95+
$isUserInGroup = empty($this->groupName) || $this->isUserInGroup($this->groupName, $user, $vo);
96+
$memberStatus = $this->adapter->getMemberStatusByUserAndVo($user, $vo);
97+
98+
if (Member::VALID === $memberStatus && $isUserInGroup) {
99+
Logger::debug(self::LOG_PREFIX . 'User is allowed to continue');
100+
return;
101+
}
102+
103+
$memberStatus = $this->rpcAdapter->getMemberStatusByUserAndVo($user, $vo);
104+
$groupHasRegistrationForm = !empty($this->groupName) && $this->groupHasRegistrationForm($vo, $this->groupName);
105+
106+
if (Member::VALID === $memberStatus && !$isUserInGroup && $groupHasRegistrationForm) {
107+
Logger::debug(self::LOG_PREFIX . 'User is not valid in group ' . $this->groupName . ' - sending to registration');
108+
$this->register($request, $this->groupName);
109+
} elseif (Member::EXPIRED === $memberStatus && $isUserInGroup) {
110+
Logger::debug(self::LOG_PREFIX . 'User is expired - sending to registration');
111+
$this->register($request);
112+
} elseif (Member::EXPIRED === $memberStatus && !$isUserInGroup && $groupHasRegistrationForm) {
113+
Logger::debug(self::LOG_PREFIX . 'User is expired and is not in group ' . $this->groupName . ' - sending to registration');
114+
$this->register($request, $this->groupName);
115+
} else {
116+
Logger::debug(self::LOG_PREFIX . 'User is not valid in vo/group and cannot be sent to the registration - sending to unauthorized');
117+
PerunIdentity::unauthorized($request);
118+
}
119+
}
120+
121+
private function isUserInGroup($groupName, $user, $vo): bool
122+
{
123+
$memberGroups = $this->adapter->getGroupsWhereMemberIsActive($user, $vo);
124+
125+
foreach ($memberGroups as $group) {
126+
if ($groupName === $group->getName()) {
127+
return true;
128+
}
129+
}
130+
131+
return false;
132+
}
133+
134+
private function groupHasRegistrationForm($vo, $groupName): bool
135+
{
136+
try {
137+
$group = $this->adapter->getGroupByName($vo, $groupName);
138+
} catch (Exception $e) {
139+
$group = null;
140+
}
141+
142+
if (null !== $group) {
143+
return $this->rpcAdapter->hasRegistrationForm($group->getId(), self::GROUP);
144+
}
145+
146+
return false;
147+
}
148+
149+
private function register(array &$request, $groupName = null): void
150+
{
151+
$request[PerunConstants::CONTINUE_FILTER_CONFIG] = $this->config;
152+
$stateId = State::saveState($request, self::STAGE);
153+
154+
$callback = Module::getModuleURL(self::CALLBACK, [
155+
self::PARAM_STATE_ID => $stateId,
156+
]);
157+
158+
Logger::debug(self::LOG_PREFIX . 'Produced callback URL \'' . $callback . '\'');
159+
$params = [];
160+
161+
if (!empty($this->callbackParameterName)) {
162+
$registrationUrl = $this->registerUrl . '?vo=' . $this->voShortName;
163+
if (null !== $groupName) {
164+
$registrationUrl .= '&group=' . $groupName;
165+
}
166+
167+
$params[PerunConstants::TARGET_NEW] = $callback;
168+
$params[PerunConstants::TARGET_EXISTING] = $callback;
169+
$params[PerunConstants::TARGET_EXTENDED] = $callback;
170+
171+
Logger::debug(
172+
self::LOG_PREFIX . 'Redirecting to \'' . $registrationUrl . ', callback parameter \'' . $this->callbackParameterName . '\' set to value \'' . $callback . '\'.'
173+
);
174+
175+
HTTP::redirectTrustedURL($registrationUrl, $params);
176+
} else {
177+
throw new Exception(self::LOG_PREFIX . 'No configuration for registration set. Cannot proceed.');
178+
}
179+
}
180+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php declare(strict_types=1);
2+
3+
use SimpleSAML\Module\perun\Auth\Process\PerunEnsureMember;
4+
5+
$this->includeAtTemplateBase('includes/header.php');
6+
7+
?>
8+
<div class="row">
9+
<div class="offset-1 col-10 offset-sm-1 col-sm-10 offset-md-2 col-md-8 offset-lg-3 col-lg-6 offset-xl-3 col-xl-6">
10+
<p><?php echo $this->t('{perun:perun:register_text}'); ?></p>
11+
<a class="btn btn-block" href="<?php echo $this->data[PerunEnsureMember::PARAM_REGISTRATION_URL]; ?>">
12+
<?php echo $this->t('{perun:perun:register_button}'); ?>
13+
</a>
14+
</div>
15+
</div>
16+
<?php
17+
18+
$this->includeAtTemplateBase('includes/footer.php');

www/perun_ensure_member.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use SimpleSAML\Configuration;
6+
use SimpleSAML\Module\perun\Auth\Process\PerunEnsureMember;
7+
use SimpleSAML\XHTML\Template;
8+
9+
$config = Configuration::getInstance();
10+
$t = new Template($config, PerunEnsureMember::TEMPLATE);
11+
$t->data[PerunEnsureMember::PARAM_REGISTRATION_URL] = $_REQUEST[PerunEnsureMember::PARAM_REGISTRATION_URL];
12+
13+
$t->show();
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use SimpleSAML\Auth\ProcessingChain;
6+
use SimpleSAML\Auth\State;
7+
use SimpleSAML\Error\BadRequest;
8+
use SimpleSAML\Module\perun\Auth\Process\PerunEnsureMember;
9+
use SimpleSAML\Module\perun\PerunConstants;
10+
11+
if (empty($_REQUEST[PerunEnsureMember::PARAM_STATE_ID])) {
12+
throw new BadRequest('Missing required \'' . PerunEnsureMember::PARAM_STATE_ID . '\' query parameter.');
13+
}
14+
15+
$state = State::loadState($_REQUEST[PerunEnsureMember::PARAM_STATE_ID], PerunEnsureMember::STAGE);
16+
17+
$filterConfig = $state[PerunConstants::CONTINUE_FILTER_CONFIG];
18+
19+
$perunEnsureMember = new PerunEnsureMember($filterConfig, null);
20+
$perunEnsureMember->process($state);
21+
22+
// we have not been redirected, continue processing
23+
ProcessingChain::resumeProcessing($state);

0 commit comments

Comments
 (0)