Skip to content

Commit

Permalink
Merge pull request #259 from LDAPAccountManager/feature/requestAccess
Browse files Browse the repository at this point in the history
Feature/request access
  • Loading branch information
gruberroland authored Nov 24, 2023
2 parents 2f987a8 + 79254d7 commit 771f7c4
Show file tree
Hide file tree
Showing 16 changed files with 319 additions and 35 deletions.
1 change: 1 addition & 0 deletions lam/HISTORY
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ December 2023 8.6
- LAM requires PHP 8.0.2 or later
- Docker: upgrade to Debian 12
- LAM Pro:
-> Request access: new module to allow users to request group memberships via self service
-> Custom scripts: support to specify the subtype of an account


Expand Down
3 changes: 2 additions & 1 deletion lam/config/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ config.cfg
/serverCerts.pem
/pdf/
/profiles/
*.sqlite
*.sqlite
*.sqlite-journal
139 changes: 139 additions & 0 deletions lam/docs/manual-sources/chapter-selfService.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1878,6 +1878,145 @@
<emphasis>uid=&lt;user name&gt;,...</emphasis></para>
</section>

<section>
<title>Request Access</title>

<para>Use this feature to allow your users to request access for group
memberships. Requests will require the approval by the group owners and
optionally a special approver group (leave empty for owner approval
only).</para>

<para><emphasis role="bold">Module Configuration</emphasis></para>

<para>First, the request access module needs to be activated and
configured on tab "Module settings". Here tick "Enable request access"
and provide the information where your groups are located.</para>

<para>Group of names and group of unique names are supported. The LDAP
filter is optional, LAM will offer the user only group of (unique) names
that have defined owners.<screenshot>
<graphic fileref="images/mod_requestAccess1.png"/>
</screenshot></para>

<para><screenshot>
<graphic fileref="images/mod_requestAccess1a.png"/>
</screenshot>The email body texts support wildcards. You can use group
owner/approver LDAP attributes in the form @@attribute@@ (e.g. @@uid@@
for the user name).</para>

<para>The requester's LDAP attributes can be used in the form
$$attribute$$ (e.g. $$uid$$ for the user name). This is supported for
mails to the group owners and the approval/deny mails to the
requester.</para>

<para>The wildcard $$requested_groups$$ will resolve to the requested
groups. This is available for mails to the group owners and the
approval/deny mails to the requester.</para>

<para>The wildcard $$requester_notes$$ resolves to the requester's
optional notes. This is available for the mails to the group
owners.</para>

<para><emphasis role="bold">Example for owner email:</emphasis></para>

<literallayout>
Dear @@cn@@,

a new access request was created by $$cn$$:

Requested groups: $$requested_groups$$

Reason: $$requester_notes$$

Please open LAM Pro's self service for details.

Best regards,
IT team

</literallayout>

<para><emphasis role="bold">Example for approver
email:</emphasis><literallayout>
Dear @@cn@@,

there are new access requests waiting for approval.

Please check here(link to self service).

Best regards,
IT team

</literallayout></para>

<para><emphasis role="bold">Example for approved/denied request
email:</emphasis><literallayout>
Dear $$cn$$,

your access request was approved.
Requested groups: $$requested_groups$$

Best regards,
IT team

</literallayout></para>

<para><emphasis role="bold">Field Configuration</emphasis></para>

<para>Next, the fields need to be added to the "Page layout" tab. There
are three fields:</para>

<itemizedlist>
<listitem>
<para>Request Access: Request access - User view that allows to
initiate the process.</para>
</listitem>

<listitem>
<para>Request Access: Owner view - Owner view for group
owners.</para>
</listitem>

<listitem>
<para>Request Access: Approver view - Approver view for approver
group</para>
</listitem>
</itemizedlist>

<para>You can set custom labels using the pencil icon.</para>

<screenshot>
<graphic fileref="images/mod_requestAccess2.png"/>
</screenshot>

<para><emphasis role="bold">Request view</emphasis></para>

<para>The user sees a button to open the new request dialog. Here the
groups can be selected and an optional note can be provided.</para>

<screenshot>
<graphic fileref="images/mod_requestAccess3.png"/>
</screenshot>

<screenshot>
<graphic fileref="images/mod_requestAccess4.png"/>
</screenshot>

<para><emphasis role="bold">Approval view</emphasis></para>

<para>Once the request is created, all owners of the respective groups
get an email notification. They can then enter self service and view
their open requests.</para>

<para>If an approver group is configured then its members will get an
email notification after owner approval. In case no approver group is
configured, the permissions are directly granted when the owner approves
the request.</para>

<screenshot>
<graphic fileref="images/mod_requestAccess5.png"/>
</screenshot>
</section>

<section>
<title>Custom fields</title>

Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
17 changes: 14 additions & 3 deletions lam/lib/html.inc
Original file line number Diff line number Diff line change
Expand Up @@ -2076,7 +2076,7 @@ class htmlInputCheckbox extends htmlElement {
$onClick = ' onclick="' . $this->onClick . '"';
}
echo '<input type="checkbox" id="' . $this->name . '" name="' . $this->name . '"' . $classes
. $this->getAccessibilityMarkup() . $onChange . $onClick . $title . $checked . $disabled . '>';
. $this->getAccessibilityMarkup() . $this->getDataAttributesAsString() . $onChange . $onClick . $title . $checked . $disabled . '>';
echo $script;
if ($this->transient) {
return array();
Expand Down Expand Up @@ -4093,6 +4093,8 @@ class htmlResponsiveInputTextarea extends htmlInputTextarea {
private $helpModule;
/** render HTML of parent class */
private $renderParentHtml = false;
/** short label */
private $shortLabel = false;

/**
* Constructor.
Expand Down Expand Up @@ -4143,7 +4145,8 @@ class htmlResponsiveInputTextarea extends htmlInputTextarea {
$helpLinkLabel->setCSSClasses($helpCssClasses);
$labelGroup->addElement($helpLinkLabel);
}
$row->add($labelGroup, 12, 6, 6, 'responsiveLabel');
$tabletDesktopLabelColumns = $this->shortLabel ? 4 : 6;
$row->add($labelGroup, 12, $tabletDesktopLabelColumns, $tabletDesktopLabelColumns, 'responsiveLabel');
// input field
$fieldGroup = new htmlGroup();
$fieldGroup->addElement($this);
Expand All @@ -4152,10 +4155,18 @@ class htmlResponsiveInputTextarea extends htmlInputTextarea {
$helpLink->setCSSClasses(array('align-top', 'hide-on-mobile'));
$fieldGroup->addElement($helpLink);
}
$row->add($fieldGroup, 12, 6, 6, 'responsiveField nowrap');
$tabletDesktopFieldColumns = $this->shortLabel ? 8 : 6;
$row->add($fieldGroup, 12, $tabletDesktopFieldColumns, $tabletDesktopFieldColumns, 'responsiveField nowrap');
return $row->generateHTML($module, $input, $values, $restricted, $scope);
}

/**
* Use a short label (4 columns instead of 6) for tablet/desktop.
*/
public function setShortLabel() {
$this->shortLabel = true;
}

}

/**
Expand Down
1 change: 1 addition & 0 deletions lam/lib/modules/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,6 @@
/mitKerberosPolicy.inc
/openldapTotp.inc
/powerDNS.inc
/requestAccess.inc
/simpleSecurityObject.inc
/guacamole.inc
53 changes: 28 additions & 25 deletions lam/lib/modules/kolabUser.inc
Original file line number Diff line number Diff line change
Expand Up @@ -703,13 +703,15 @@ class kolabUser extends baseModule {
$invitationContainer = new htmlTable();
// default invitation policy
$defaultInvPol = $this->invitationPolicies['ACT_MANUAL'];
for ($i = 0; $i < sizeof($attributes['kolabInvitationPolicy']); $i++) {
$parts = explode(":", $attributes['kolabInvitationPolicy'][$i]);
if (sizeof($parts) == 1) {
$defaultInvPol = $this->invitationPolicies[$attributes['kolabInvitationPolicy'][$i]];
unset($attributes['kolabInvitationPolicy'][$i]);
$attributes['kolabInvitationPolicy'] = array_values($attributes['kolabInvitationPolicy']);
break;
if (!empty($attributes['kolabInvitationPolicy'])) {
for ($i = 0; $i < sizeof($attributes['kolabInvitationPolicy']); $i++) {
$parts = explode(":", $attributes['kolabInvitationPolicy'][$i]);
if (sizeof($parts) == 1) {
$defaultInvPol = $this->invitationPolicies[$attributes['kolabInvitationPolicy'][$i]];
unset($attributes['kolabInvitationPolicy'][$i]);
$attributes['kolabInvitationPolicy'] = array_values($attributes['kolabInvitationPolicy']);
break;
}
}
}
$invitationContainer->addElement(new htmlOutputText(_('Anyone')));
Expand All @@ -720,24 +722,25 @@ class kolabUser extends baseModule {
$invitationContainer->addElement(new htmlOutputText($defaultInvPol), true);
}
// other invitation policies
for ($i = 0; $i < sizeof($attributes['kolabInvitationPolicy']); $i++) {
$parts = explode(":", $attributes['kolabInvitationPolicy'][$i]);
if (sizeof($parts) == 2) {
if (!in_array('kolabDelegate', $readOnlyFields)) {
$newPolicyInput = new htmlInputField('invPol1' . $i, $parts[0]);
$newPolicyInput->setAccessibilityLabel(_('Invitation policy'));
$invitationContainer->addElement($newPolicyInput);
$invitationContainer->addElement(new htmlSelect('invPol2' . $i, array_values($this->invitationPolicies), array($this->invitationPolicies[$parts[1]])));
$invPolGroup = new htmlGroup();
$delCheckbox = new htmlInputCheckbox('delInvPol' . $i, false);
$delCheckbox->setAccessibilityLabel(_("Remove"));
$invPolGroup->addElement($delCheckbox);
$invPolGroup->addElement(new htmlOutputText(_("Remove")));
$invitationContainer->addElement(new htmlDiv(null, $invPolGroup, array('nowrap')), true);
}
else {
$invitationContainer->addElement(new htmlOutputText($parts[0]));
$invitationContainer->addElement(new htmlOutputText($this->invitationPolicies[$parts[1]]), true);
if (!empty($attributes['kolabInvitationPolicy'])) {
for ($i = 0; $i < sizeof($attributes['kolabInvitationPolicy']); $i++) {
$parts = explode(":", $attributes['kolabInvitationPolicy'][$i]);
if (sizeof($parts) == 2) {
if (!in_array('kolabDelegate', $readOnlyFields)) {
$newPolicyInput = new htmlInputField('invPol1' . $i, $parts[0]);
$newPolicyInput->setAccessibilityLabel(_('Invitation policy'));
$invitationContainer->addElement($newPolicyInput);
$invitationContainer->addElement(new htmlSelect('invPol2' . $i, array_values($this->invitationPolicies), array($this->invitationPolicies[$parts[1]])));
$invPolGroup = new htmlGroup();
$delCheckbox = new htmlInputCheckbox('delInvPol' . $i, false);
$delCheckbox->setAccessibilityLabel(_("Remove"));
$invPolGroup->addElement($delCheckbox);
$invPolGroup->addElement(new htmlOutputText(_("Remove")));
$invitationContainer->addElement(new htmlDiv(null, $invPolGroup, array('nowrap')), true);
} else {
$invitationContainer->addElement(new htmlOutputText($parts[0]));
$invitationContainer->addElement(new htmlOutputText($this->invitationPolicies[$parts[1]]), true);
}
}
}
}
Expand Down
12 changes: 6 additions & 6 deletions lam/lib/selfService.inc
Original file line number Diff line number Diff line change
Expand Up @@ -100,21 +100,21 @@ function getSelfServiceFieldSettings($scope) {
function getSelfServiceOptions($scope, $fields, $attributes, $passwordChangeOnly, $readOnlyFields) {
$return = array();
$modules = getAvailableModules($scope);
for ($i = 0; $i < sizeof($modules); $i++) {
if (!isset($fields[$modules[$i]])) {
foreach ($modules as $module) {
if (!isset($fields[$module])) {
continue;
}
$m = moduleCache::getModule($modules[$i], $scope);
$m = moduleCache::getModule($module, $scope);
$modReadOnlyFields = array();
for ($r = 0; $r < sizeof($readOnlyFields); $r++) {
$parts = explode('_', $readOnlyFields[$r]);
if ($parts[0] == $modules[$i]) {
if ($parts[0] == $module) {
$modReadOnlyFields[] = $parts[1];
}
}
$code = $m->getSelfServiceOptions($fields[$modules[$i]], $attributes, $passwordChangeOnly, $modReadOnlyFields);
$code = $m->getSelfServiceOptions($fields[$module], $attributes, $passwordChangeOnly, $modReadOnlyFields);
if (sizeof($code) > 0) {
$return[$modules[$i]] = $code;
$return[$module] = $code;
}
}
return $return;
Expand Down
Binary file modified lam/style/loading.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
51 changes: 51 additions & 0 deletions lam/templates/lib/500_lam.js
Original file line number Diff line number Diff line change
Expand Up @@ -1048,6 +1048,36 @@ window.lam.dialog.showMessage = function(title, okText, divId, callbackFunction)
});
};

/**
* Shows a confirmation message.
*
* @param title dialog title
* @param okText ok button text
* @param cancelText cancel button text
* @param divId DIV id with dialog content
* @param callbackFunction callback function (optional, gets result of preConfirmFunction as parameter)
* @param preConfirmFunction preConfirm function (optional)
*/
window.lam.dialog.showConfirmation = async function(title, okText, cancelText, divId, callbackFunction, preConfirmFunction) {
const dialogContent = document.getElementById(divId).cloneNode(true);
dialogContent.classList.remove('hidden');
let options = {
title: title,
confirmButtonText: okText,
cancelButtonText: cancelText,
showCancelButton: true,
html: dialogContent.outerHTML,
width: 'auto'
};
if (preConfirmFunction) {
options.preConfirm = preConfirmFunction;
}
const {value: formValues} = await Swal.fire(options);
if (callbackFunction && formValues) {
callbackFunction(formValues);
}
};

/**
* Shows a simple dialog.
*
Expand Down Expand Up @@ -3259,6 +3289,27 @@ window.lam.richEdit.init = function() {
});
}

window.lam.loadingIndicator = window.lam.loadingIndicator || {};

/**
* Starts the loading indicator.
*/
window.lam.loadingIndicator.start = function() {
Swal.fire({
title:"",
imageUrl: "../../style/loading.gif",
showConfirmButton: false,
allowOutsideClick: false,
timer: 60000,
});
}

/**
* Stops the loading indicator.
*/
window.lam.loadingIndicator.stop = function() {
Swal.close();
}

jQuery(document).ready(function() {
window.lam.form.autoTrim();
Expand Down
Loading

0 comments on commit 771f7c4

Please sign in to comment.