Merge remote-tracking branch 'origin/develop' into 413-update-dependencies-webauthn

This commit is contained in:
Roland Gruber 2025-03-30 14:26:19 +02:00
commit f5172b6b5a
22 changed files with 725 additions and 159 deletions

View file

@ -1,4 +1,4 @@
This software is copyright (c) 2003 - 2024 by Roland Gruber This software is copyright (c) 2003 - 2025 by Roland Gruber
If you purchased a copy of LDAP Account Manager Pro then the following If you purchased a copy of LDAP Account Manager Pro then the following
files are licensed under the conditions which you accepted at purchase files are licensed under the conditions which you accepted at purchase
@ -17,6 +17,8 @@ time.
* lib/modules/automount.inc * lib/modules/automount.inc
* lib/modules/bindDLZ.inc * lib/modules/bindDLZ.inc
* lib/modules/bindDLZXfr.inc * lib/modules/bindDLZXfr.inc
* lib/modules/bindDyndbRecord.inc
* lib/modules/bindDyndbZone.inc
* lib/modules/customBaseType.inc * lib/modules/customBaseType.inc
* lib/modules/customFields.inc * lib/modules/customFields.inc
* lib/modules/customScripts.inc * lib/modules/customScripts.inc
@ -56,6 +58,7 @@ time.
* lib/modules/rfc2307bisAutomount.inc * lib/modules/rfc2307bisAutomount.inc
* lib/modules/rfc2307bisPosixGroup.inc * lib/modules/rfc2307bisPosixGroup.inc
* lib/modules/selfRegistration.inc * lib/modules/selfRegistration.inc
* lib/modules/simpleSecurityObject.inc
* lib/modules/sudoRole.inc * lib/modules/sudoRole.inc
* lib/modules/uidObject.inc * lib/modules/uidObject.inc
* lib/modules/webauthn.inc * lib/modules/webauthn.inc
@ -64,6 +67,7 @@ time.
* lib/types/alias.inc * lib/types/alias.inc
* lib/types/automountType.inc * lib/types/automountType.inc
* lib/types/bind.inc * lib/types/bind.inc
* lib/types/bindDyndbType.inc
* lib/types/customType.inc * lib/types/customType.inc
* lib/types/gon.inc * lib/types/gon.inc
* lib/types/kopanoAddressListType.inc * lib/types/kopanoAddressListType.inc

View file

@ -1,6 +1,9 @@
June 2025 9.2 June 2025 9.2
- TAK support added
- Active Directory: allow to restore deleted entries in tree view (415) - Active Directory: allow to restore deleted entries in tree view (415)
- Fixed bugs:
-> Unix: profile editor for users not working (418)
-> Custom fields: problems with deleting facsimileTelephoneNumber (419)
13.03.2025 9.1 13.03.2025 9.1
- Usability improvements (347, 348, 360, 403) - Usability improvements (347, 348, 360, 403)

View file

@ -17,6 +17,8 @@ time.
* lib/modules/automount.inc * lib/modules/automount.inc
* lib/modules/bindDLZ.inc * lib/modules/bindDLZ.inc
* lib/modules/bindDLZXfr.inc * lib/modules/bindDLZXfr.inc
* lib/modules/bindDyndbRecord.inc
* lib/modules/bindDyndbZone.inc
* lib/modules/customBaseType.inc * lib/modules/customBaseType.inc
* lib/modules/customFields.inc * lib/modules/customFields.inc
* lib/modules/customScripts.inc * lib/modules/customScripts.inc
@ -56,6 +58,7 @@ time.
* lib/modules/rfc2307bisAutomount.inc * lib/modules/rfc2307bisAutomount.inc
* lib/modules/rfc2307bisPosixGroup.inc * lib/modules/rfc2307bisPosixGroup.inc
* lib/modules/selfRegistration.inc * lib/modules/selfRegistration.inc
* lib/modules/simpleSecurityObject.inc
* lib/modules/sudoRole.inc * lib/modules/sudoRole.inc
* lib/modules/uidObject.inc * lib/modules/uidObject.inc
* lib/modules/webauthn.inc * lib/modules/webauthn.inc
@ -64,6 +67,7 @@ time.
* lib/types/alias.inc * lib/types/alias.inc
* lib/types/automountType.inc * lib/types/automountType.inc
* lib/types/bind.inc * lib/types/bind.inc
* lib/types/bindDyndbType.inc
* lib/types/customType.inc * lib/types/customType.inc
* lib/types/gon.inc * lib/types/gon.inc
* lib/types/kopanoAddressListType.inc * lib/types/kopanoAddressListType.inc
@ -433,7 +437,7 @@ E:
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
F: F:
3-Clause BSD License 3-Clause BSD License
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without

View file

@ -467,7 +467,7 @@
<entry>dhcp.schema</entry> <entry>dhcp.schema</entry>
<entry>docs/schema/dhcp.schema</entry> <entry>Part of LAM installation: docs/schema/dhcp.schema</entry>
<entry>The LDAP suffix should be set to your dhcpServer <entry>The LDAP suffix should be set to your dhcpServer
entry.</entry> entry.</entry>
@ -486,7 +486,7 @@
<entry>schema.ldif</entry> <entry>schema.ldif</entry>
<entry>part of bind-dyndb-ldap</entry> <entry>Part of bind-dyndb-ldap</entry>
<entry>LAM Pro only</entry> <entry>LAM Pro only</entry>
</row> </row>
@ -505,7 +505,7 @@
<entry>dlz.schema</entry> <entry>dlz.schema</entry>
<entry>part of <ulink url="http://bind-dlz.sourceforge.net/">Bind <entry>Part of <ulink url="http://bind-dlz.sourceforge.net/">Bind
DLZ patch</ulink></entry> DLZ patch</ulink></entry>
<entry>LAM Pro only</entry> <entry>LAM Pro only</entry>
@ -821,6 +821,24 @@
<entry>LAM Pro only, requires DDS extension on LDAP server <entry>LAM Pro only, requires DDS extension on LDAP server
side</entry> side</entry>
</row> </row>
<row>
<entry><inlinemediaobject>
<imageobject>
<imagedata fileref="images/schema_tak.png" width="16px"/>
</imageobject>
</inlinemediaobject></entry>
<entry>TAK</entry>
<entry>takUser</entry>
<entry>tak-*.ldif</entry>
<entry>Part of LAM installation: docs/schema/tak-*.ldif</entry>
<entry/>
</row>
</tbody> </tbody>
</tgroup> </tgroup>
</table> </table>

View file

@ -2510,6 +2510,56 @@ AuthorizedKeysCommandUser root</literallayout>
<graphic fileref="images/mod_lastBind3.png"/> <graphic fileref="images/mod_lastBind3.png"/>
</screenshot> </screenshot>
</section> </section>
<section>
<title>TAK</title>
<para>The <ulink url="https://www.civtak.org/">TAK</ulink> module
supports the Team Awareness Kit or Tactical Assault Kit (TAK) with the
Android Team Awareness Kit (ATAK).</para>
<para>You can define callsigns, team roles and colors for users.</para>
<para><emphasis role="bold">LDAP schema</emphasis></para>
<para>The module expects that TAK users use the object class "takUser"
and the attributes "takCallsign", "takRole" and "takColor". You can find
matching schema files in /usr/share/ldap-account-manager/docs/schema
(DEB/RPM) or docs/schema (tar.bz2). Please see the beginning of the
files for installation instructions.</para>
<itemizedlist>
<listitem>
<para>OpenLDAP: tak-OpenLDAP.ldif</para>
</listitem>
<listitem>
<para>Samba 4: tak-Samba4-attributes.ldif and
tak-Samba4-objectClass.ldif</para>
</listitem>
<listitem>
<para>Windows (AD): tak-Windows.ldif</para>
</listitem>
</itemizedlist>
<para><emphasis role="bold">Configuration</emphasis></para>
<para>Add the TAK module for users in your server profile:</para>
<screenshot>
<graphic fileref="images/mod_tak1.png"/>
</screenshot>
<para>Now you can manage the TAK attributes for users.</para>
<para>LAM Pro users can add these attributes to the self-service profile
if needed.</para>
<screenshot>
<graphic fileref="images/mod_tak2.png"/>
</screenshot>
</section>
</section> </section>
<section> <section>

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -0,0 +1,33 @@
#
# LDAP schema for LAM TAK functionality
#
# This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/)
# Copyright (C) 2025 Roland Gruber
#
#
# OID bases:
# 1.3.6.1.4.1.34955 Roland Gruber Softwareentwicklung
# 1.3.6.1.4.1.34955.1 attributes
# 1.3.6.1.4.1.34955.2 object classes
#
# Installation:
# ldapadd -x -W -H ldap://localhost -D "cn=admin,dc=company,dc=com" -f tak-OpenLDAP.ldif
#
# Please replace "localhost" with your LDAP server and "cn=admin,dc=company,dc=com" with your LDAP admin user (usually starts with cn=admin or cn=manager).
#
# In some cases you might need to import directly on the OpenLDAP server as root:
# ldapadd -Y EXTERNAL -H ldapi:/// -f tak-OpenLDAP.ldif
#
# Version: 1
#
# Changelog:
# 1: initial release (LAM 9.2)
#
dn: cn=tak,cn=schema,cn=config
objectClass: olcSchemaConfig
cn: tak
olcAttributeTypes: ( 1.3.6.1.4.1.34955.1.100 NAME 'takCallsign' DESC 'TAK callsign' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE )
olcAttributeTypes: ( 1.3.6.1.4.1.34955.1.101 NAME 'takRole' DESC 'TAK team role' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE )
olcAttributeTypes: ( 1.3.6.1.4.1.34955.1.102 NAME 'takColor' DESC 'TAK team color' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE )
olcObjectClasses: ( 1.3.6.1.4.1.34955.2.10 NAME 'takUser' DESC 'TAK user' SUP top AUXILIARY MAY ( takCallsign $ takRole $ takColor ) MUST ( cn ) )

View file

@ -0,0 +1,58 @@
#
# LDAP schema for LAM TAK functionality
#
# This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/)
# Copyright (C) 2025 Roland Gruber
#
#
# OID bases:
# 1.3.6.1.4.1.34955 Roland Gruber Softwareentwicklung
# 1.3.6.1.4.1.34955.1 attributes
# 1.3.6.1.4.1.34955.2 object classes
#
# Please replace DOMAIN_TOP_DN with your LDAP suffix (e.g. dc=samba4,dc=test).
# This file must be installed first.
#
# Installation: ldbmodify -H /var/lib/samba/private/sam.ldb tak-Samba4-attributes.ldif --option="dsdb:schema update allowed"=true
#
#
# Version: 1
# 1: initial release (LAM 9.2)
#
dn: CN=takCallsign,CN=Schema,CN=Configuration,DOMAIN_TOP_DN
objectClass: top
objectClass: attributeSchema
attributeID: 1.3.6.1.4.1.34955.1.100
attributeSyntax: 2.5.5.12
oMSyntax: 64
isSingleValued: TRUE
rangeLower: 4
cn: takCallsign
name: takCallsign
lDAPDisplayName: takCallsign
description: TAK callsign
dn: CN=takRole,CN=Schema,CN=Configuration,DOMAIN_TOP_DN
objectClass: top
objectClass: attributeSchema
attributeID: 1.3.6.1.4.1.34955.1.101
attributeSyntax: 2.5.5.12
oMSyntax: 64
isSingleValued: TRUE
cn: takRole
name: takRole
lDAPDisplayName: takRole
description: TAK team role
dn: CN=takColor,CN=Schema,CN=Configuration,DOMAIN_TOP_DN
objectClass: top
objectClass: attributeSchema
attributeID: 1.3.6.1.4.1.34955.1.102
attributeSyntax: 2.5.5.12
oMSyntax: 64
isSingleValued: TRUE
cn: takColor
name: takColor
LDAPDisplayName: takColor
Description: TAK team color

View file

@ -0,0 +1,36 @@
#
# LDAP schema for LAM TAK functionality
#
# This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/)
# Copyright (C) 2025 Roland Gruber
#
#
# OID bases:
# 1.3.6.1.4.1.34955 Roland Gruber Softwareentwicklung
# 1.3.6.1.4.1.34955.1 attributes
# 1.3.6.1.4.1.34955.2 object classes
#
# Please replace DOMAIN_TOP_DN with your LDAP suffix (e.g. dc=samba4,dc=test).
# This file must be installed second.
#
# Installation: ldbmodify -H /var/lib/samba/private/sam.ldb tak-Samba4-objectClass.ldif --option="dsdb:schema update allowed"=true
#
#
# Version: 1
# 1: initial release (LAM 9.2)
#
dn: CN=takUser,CN=Schema,CN=Configuration,DOMAIN_TOP_DN
objectClass: top
objectClass: classSchema
governsID: 1.3.6.1.4.1.34955.2.10
cn: takUser
lDAPDisplayName: takUser
subClassOf: top
objectClassCategory: 3
mustContain: cn
mayContain: takCallsign
mayContain: takRole
mayContain: takColor
description: TAK user
possSuperiors: top

View file

@ -0,0 +1,100 @@
#
# LDAP schema for LAM TAK functionality
#
# This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/)
# Copyright (C) 2025 Roland Gruber
#
#
# OID bases:
# 1.3.6.1.4.1.34955 Roland Gruber Softwareentwicklung
# 1.3.6.1.4.1.34955.1 attributes
# 1.3.6.1.4.1.34955.2 object classes
#
# Please replace DOMAIN_TOP_DN with your LDAP suffix (e.g. dc=windows,dc=test).
#
# Installation: ldifde -v -i -f tak-Windows.ldif
#
#
# Version: 1
# 1: initial release (LAM 9.2)
#
dn: CN=takCallsign,CN=Schema,CN=Configuration,DOMAIN_TOP_DN
changetype: add
objectClass: top
objectClass: attributeSchema
attributeID: 1.3.6.1.4.1.34955.1.100
attributeSyntax: 2.5.5.12
oMSyntax: 64
isSingleValued: TRUE
rangeLower: 4
cn: takCallsign
name: takCallsign
lDAPDisplayName: takCallsign
description: TAK callsign
dn: CN=takRole,CN=Schema,CN=Configuration,DOMAIN_TOP_DN
changetype: add
objectClass: top
objectClass: attributeSchema
attributeID: 1.3.6.1.4.1.34955.1.101
attributeSyntax: 2.5.5.12
oMSyntax: 64
isSingleValued: TRUE
cn: takRole
name: takRole
lDAPDisplayName: takRole
description: TAK team role
dn: CN=takColor,CN=Schema,CN=Configuration,DOMAIN_TOP_DN
changetype: add
objectClass: top
objectClass: attributeSchema
attributeID: 1.3.6.1.4.1.34955.1.102
attributeSyntax: 2.5.5.12
oMSyntax: 64
isSingleValued: TRUE
cn: takColor
name: takColor
LDAPDisplayName: takColor
Description: TAK team color
dn:
changetype: modify
add: schemaUpdateNow
schemaUpdateNow: 1
-
dn: CN=takUser,CN=Schema,CN=Configuration,DOMAIN_TOP_DN
changetype: add
objectClass: top
objectClass: classSchema
governsID: 1.3.6.1.4.1.34955.2.10
cn: takUser
lDAPDisplayName: takUser
subClassOf: top
objectClassCategory: 3
mustContain: cn
mayContain: takCallsign
mayContain: takRole
mayContain: takColor
description: TAK user
possSuperiors: top
dn:
changetype: modify
add: schemaUpdateNow
schemaUpdateNow: 1
-
dn: CN=User,CN=Schema,CN=Configuration,DOMAIN_TOP_DN
changetype: modify
add: auxiliaryClass
auxiliaryClass: takUser
-
dn:
changetype: modify
add: schemaUpdateNow
schemaUpdateNow: 1
-

View file

@ -3,7 +3,7 @@
This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/) This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/)
Copyright (C) 2003 - 2006 Tilo Lutz Copyright (C) 2003 - 2006 Tilo Lutz
2009 - 2024 Roland Gruber 2009 - 2025 Roland Gruber
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -77,6 +77,18 @@ function in_array_ignore_case($needle, $haystack) {
return false; return false;
} }
/**
* Checks if two arrays have the same content.
*
* @param array $array1 array 1
* @param array $array2 array 2
* @return bool same content
*/
function areArrayContentsEqual(array $array1, array $array2): bool {
$intersect = array_intersect($array1, $array2);
return ((count($array1) === count($array2)) && (count($intersect) === count($array1)));
}
/** /**
* Sorts an array in natural order by its keys. * Sorts an array in natural order by its keys.
* *

View file

@ -9,7 +9,7 @@ use LAM\PDF\PDFImage;
/* /*
This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/) This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/)
Copyright (C) 2003 - 2024 Roland Gruber Copyright (C) 2003 - 2025 Roland Gruber
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -1034,7 +1034,7 @@ abstract class baseModule {
if (!empty($regex)) { if (!empty($regex)) {
$this->checkUploadRegex($regexIDs, $rawAccounts[$position][$ids[$colName]], $message, $position, $errors); $this->checkUploadRegex($regexIDs, $rawAccounts[$position][$ids[$colName]], $message, $position, $errors);
} }
$partialAccounts[$position][$attrName] = trim($rawAccounts[$position][$ids[$colName]]); $partialAccounts[$position][$attrName] = [trim($rawAccounts[$position][$ids[$colName]])];
} }
// multi-value // multi-value
else { else {
@ -1799,7 +1799,7 @@ abstract class baseModule {
* @param array $container return value of checkSelfServiceOptions() * @param array $container return value of checkSelfServiceOptions()
* @param String $name attribute name * @param String $name attribute name
* @param array $attributes LDAP attributes * @param array $attributes LDAP attributes
* @param string $fields input fields * @param array $fields input fields
* @param array $readOnlyFields list of read-only fields * @param array $readOnlyFields list of read-only fields
* @param String $validationID validation ID for get_preg() * @param String $validationID validation ID for get_preg()
* @param array $validationMessage validation message data (defaults to $this->messages[$name][0]) * @param array $validationMessage validation message data (defaults to $this->messages[$name][0])
@ -1822,13 +1822,11 @@ abstract class baseModule {
$container['add'][$ldapAttrName] = [$_POST[$fieldName]]; $container['add'][$ldapAttrName] = [$_POST[$fieldName]];
} }
} }
elseif ($requiredMessage !== null) {
$container['messages'][] = $requiredMessage;
}
elseif (isset($attributes[$ldapAttrName])) { elseif (isset($attributes[$ldapAttrName])) {
if ($requiredMessage !== null) { $container['del'][$ldapAttrName] = $attributes[$ldapAttrName];
$container['messages'][] = $requiredMessage;
}
else {
$container['del'][$ldapAttrName] = $attributes[$ldapAttrName];
}
} }
} }
} }
@ -1954,8 +1952,7 @@ abstract class baseModule {
$container['messages'][] = $requiredMessage; $container['messages'][] = $requiredMessage;
} }
$valuesOld = $attributes[$ldapAttrName] ?? []; $valuesOld = $attributes[$ldapAttrName] ?? [];
$intersect = array_intersect($valuesOld, $valuesNew); if (!areArrayContentsEqual($valuesOld, $valuesNew)) {
if ((count($valuesOld) !== count($valuesNew)) || (count($intersect) !== count($valuesOld))) {
$container['mod'][$ldapAttrName] = $valuesNew; $container['mod'][$ldapAttrName] = $valuesNew;
} }
} }

View file

@ -1589,87 +1589,49 @@ class accountContainer {
*/ */
function save_module_attributes($attributes, $orig) { function save_module_attributes($attributes, $orig) {
$return = []; $return = [];
$toadd = []; $toModify = [];
$tomodify = []; $notChanged = [];
$torem = []; // cleanup
$notchanged = []; foreach ($attributes as $name => &$values) {
// get list of all attributes // remove empty values
$attr_names = array_keys($attributes); $values = array_values($values);
$orig_names = array_keys($orig); for ($i = 0; $i < count($values); $i++) {
if ($values[$i] === '') {
unset($values[$i]);
}
}
$values = array_values($values);
// remove empty list of values
if (sizeof($values) === 0) {
unset($attributes[$name]);
}
}
// find deleted attributes (in $orig but no longer in $attributes) // find deleted attributes (in $orig but no longer in $attributes)
foreach ($orig_names as $value) { foreach ($orig as $name => $value) {
if (!isset($attributes[$value])) { if (!isset($attributes[$name]) || (sizeof($attributes[$name]) === 0)) {
$torem[$value] = $orig[$value]; $toModify[$name] = [];
} }
} }
// find changed attributes // find changed attributes
foreach ($attr_names as $name) { foreach ($attributes as $name => $value) {
// find deleted attributes // new attributes
if (isset($orig[$name]) && is_array($orig[$name])) { if (!isset($orig[$name])) {
foreach ($orig[$name] as $value) { $toModify[$name] = $value;
if (is_array($attributes[$name])) {
if (!in_array($value, $attributes[$name], true)
&& ($value !== null)
&& ($value !== '')) {
$torem[$name][] = $value;
}
}
elseif (($value !== null) && ($value !== '')) {
$torem[$name][] = $value;
}
}
} }
// find new attributes // changed attributes
if (isset($attributes[$name]) && is_array($attributes[$name])) { elseif (!areArrayContentsEqual($value, $orig[$name])) {
foreach ($attributes[$name] as $value) { $toModify[$name] = $value;
if (isset($orig[$name]) && is_array($orig[$name])) {
if (!in_array($value, $orig[$name], true)
&& ($value !== null)
&& ($value !== '')) {
$toadd[$name][] = $value;
}
}
elseif (($value !== null) && ($value !== '')) {
$toadd[$name][] = $value;
}
}
} }
// find unchanged attributes // unchanged attributes
if (isset($orig[$name]) && is_array($orig[$name]) && is_array($attributes[$name])) { else {
foreach ($attributes[$name] as $value) { $notChanged[$name] = $value;
if (($value !== null) && ($value !== '') && in_array($value, $orig[$name], true)) {
$notchanged[$name][] = $value;
}
}
} }
} }
// create modify with add and remove if ($toModify !== []) {
$attributes2 = array_keys($toadd); $return[$this->dn_orig]['modify'] = $toModify;
for ($i = 0; $i < count($attributes2); $i++) {
if (isset($torem[$attributes2[$i]]) && ($toadd[$attributes2[$i]] !== []) && (count($torem[$attributes2[$i]]) > 0)) {
// found attribute which should be modified
$tomodify[$attributes2[$i]] = $toadd[$attributes2[$i]];
// merge unchanged values
if (isset($notchanged[$attributes2[$i]])) {
$tomodify[$attributes2[$i]] = array_merge($tomodify[$attributes2[$i]], $notchanged[$attributes2[$i]]);
unset($notchanged[$attributes2[$i]]);
}
// remove old add and remove commands
unset($toadd[$attributes2[$i]]);
unset($torem[$attributes2[$i]]);
}
} }
if ($toadd !== []) { if ($notChanged !== []) {
$return[$this->dn_orig]['add'] = $toadd; $return[$this->dn_orig]['notchanged'] = $notChanged;
}
if ($torem !== []) {
$return[$this->dn_orig]['remove'] = $torem;
}
if ($tomodify !== []) {
$return[$this->dn_orig]['modify'] = $tomodify;
}
if ($notchanged !== []) {
$return[$this->dn_orig]['notchanged'] = $notchanged;
} }
return $return; return $return;
} }

View file

@ -894,28 +894,6 @@ class inetOrgPerson extends baseModule implements passwordService, AccountStatus
return []; return [];
} }
$return = parent::save_attributes(); $return = parent::save_attributes();
// postalAddress, registeredAddress, facsimileTelephoneNumber and jpegPhoto need special removing
if (isset($return[$this->getAccountContainer()->dn_orig]['remove']['postalAddress'])) {
$return[$this->getAccountContainer()->dn_orig]['modify']['postalAddress'] = $this->attributes['postalAddress'];
unset($return[$this->getAccountContainer()->dn_orig]['remove']['postalAddress']);
}
if (isset($return[$this->getAccountContainer()->dn_orig]['remove']['registeredAddress'])) {
$return[$this->getAccountContainer()->dn_orig]['modify']['registeredAddress'] = $this->attributes['registeredAddress'];
unset($return[$this->getAccountContainer()->dn_orig]['remove']['registeredAddress']);
}
if (isset($return[$this->getAccountContainer()->dn_orig]['remove']['facsimileTelephoneNumber'])) {
$return[$this->getAccountContainer()->dn_orig]['modify']['facsimileTelephoneNumber'] = $this->attributes['facsimileTelephoneNumber'];
unset($return[$this->getAccountContainer()->dn_orig]['remove']['facsimileTelephoneNumber']);
}
if (isset($return[$this->getAccountContainer()->dn_orig]['add']['facsimileTelephoneNumber'])
&& isset($this->orig['facsimileTelephoneNumber']) && (count($this->orig['facsimileTelephoneNumber']) > 0)) {
$return[$this->getAccountContainer()->dn_orig]['modify']['facsimileTelephoneNumber'] = $this->attributes['facsimileTelephoneNumber'];
unset($return[$this->getAccountContainer()->dn_orig]['add']['facsimileTelephoneNumber']);
}
if (isset($return[$this->getAccountContainer()->dn_orig]['remove']['jpegPhoto'])) {
$return[$this->getAccountContainer()->dn_orig]['modify']['jpegPhoto'] = [];
unset($return[$this->getAccountContainer()->dn_orig]['remove']['jpegPhoto']);
}
// add information about clear text password // add information about clear text password
if ($this->clearTextPassword != null) { if ($this->clearTextPassword != null) {
$return[$this->getAccountContainer()->dn_orig]['info']['userPasswordClearText'][0] = $this->clearTextPassword; $return[$this->getAccountContainer()->dn_orig]['info']['userPasswordClearText'][0] = $this->clearTextPassword;
@ -2730,19 +2708,19 @@ class inetOrgPerson extends baseModule implements passwordService, AccountStatus
$attributes, $readOnlyFields, false, false, 'givenName'); $attributes, $readOnlyFields, false, false, 'givenName');
$this->addSimpleSelfServiceTextField($return, 'lastName', _('Last name'), $fields, $this->addSimpleSelfServiceTextField($return, 'lastName', _('Last name'), $fields,
$attributes, $readOnlyFields, true, false, 'sn'); $attributes, $readOnlyFields, true, false, 'sn');
$this->addSimpleSelfServiceTextField($return, 'mail', _('Email address'), $fields, $this->addMultiValueSelfServiceTextField($return, 'mail', _('Email address'), $fields,
$attributes, $readOnlyFields); $attributes, $readOnlyFields);
$this->addMultiValueSelfServiceTextField($return, 'labeledURI', _('Web site'), $fields, $this->addMultiValueSelfServiceTextField($return, 'labeledURI', _('Web site'), $fields,
$attributes, $readOnlyFields, false, false, 'labeledURI'); $attributes, $readOnlyFields, false, false, 'labeledURI');
$this->addSimpleSelfServiceTextField($return, 'telephoneNumber', _('Telephone number'), $fields, $this->addMultiValueSelfServiceTextField($return, 'telephoneNumber', _('Telephone number'), $fields,
$attributes, $readOnlyFields, false, false, 'telephoneNumber'); $attributes, $readOnlyFields, false, false, 'telephoneNumber');
$this->addSimpleSelfServiceTextField($return, 'homePhone', _('Home telephone number'), $fields, $this->addMultiValueSelfServiceTextField($return, 'homePhone', _('Home telephone number'), $fields,
$attributes, $readOnlyFields, false, false, 'homePhone'); $attributes, $readOnlyFields, false, false, 'homePhone');
$this->addSimpleSelfServiceTextField($return, 'mobile', _('Mobile telephone number'), $fields, $this->addMultiValueSelfServiceTextField($return, 'mobile', _('Mobile telephone number'), $fields,
$attributes, $readOnlyFields); $attributes, $readOnlyFields);
$this->addSimpleSelfServiceTextField($return, 'faxNumber', _('Fax number'), $fields, $this->addMultiValueSelfServiceTextField($return, 'faxNumber', _('Fax number'), $fields,
$attributes, $readOnlyFields, false, false, 'facsimileTelephoneNumber'); $attributes, $readOnlyFields, false, false, 'facsimileTelephoneNumber');
$this->addSimpleSelfServiceTextField($return, 'pager', _('Pager'), $fields, $this->addMultiValueSelfServiceTextField($return, 'pager', _('Pager'), $fields,
$attributes, $readOnlyFields); $attributes, $readOnlyFields);
$this->addMultiValueSelfServiceTextField($return, 'street', _('Street'), $fields, $this->addMultiValueSelfServiceTextField($return, 'street', _('Street'), $fields,
$attributes, $readOnlyFields); $attributes, $readOnlyFields);
@ -3200,22 +3178,22 @@ class inetOrgPerson extends baseModule implements passwordService, AccountStatus
$this->checkSimpleSelfServiceTextField($return, 'lastName', $attributes, $fields, $this->checkSimpleSelfServiceTextField($return, 'lastName', $attributes, $fields,
$readOnlyFields, 'realname', $this->messages['lastname'][0], $this->messages['lastname'][0], $readOnlyFields, 'realname', $this->messages['lastname'][0], $this->messages['lastname'][0],
'sn'); 'sn');
$this->checkSimpleSelfServiceTextField($return, 'mail', $attributes, $fields, $this->checkMultiValueSelfServiceTextField($return, 'mail', $attributes, $fields,
$readOnlyFields, 'email', $this->messages['mail'][0]); $readOnlyFields, 'email', $this->messages['mail'][0]);
$this->checkMultiValueSelfServiceTextField($return, 'labeledURI', $attributes, $fields, $this->checkMultiValueSelfServiceTextField($return, 'labeledURI', $attributes, $fields,
$readOnlyFields, null, null, null, 'labeledURI'); $readOnlyFields, null, null, null, 'labeledURI');
$this->checkSimpleSelfServiceTextField($return, 'telephoneNumber', $attributes, $fields, $this->checkMultiValueSelfServiceTextField($return, 'telephoneNumber', $attributes, $fields,
$readOnlyFields, 'telephone', $this->messages['telephoneNumber'][0], null, $readOnlyFields, 'telephone', $this->messages['telephoneNumber'][0], null,
'telephoneNumber'); 'telephoneNumber');
$this->checkSimpleSelfServiceTextField($return, 'homePhone', $attributes, $fields, $this->checkMultiValueSelfServiceTextField($return, 'homePhone', $attributes, $fields,
$readOnlyFields, 'telephone', $this->messages['homePhone'][0], null, $readOnlyFields, 'telephone', $this->messages['homePhone'][0], null,
'homePhone'); 'homePhone');
$this->checkSimpleSelfServiceTextField($return, 'faxNumber', $attributes, $fields, $this->checkMultiValueSelfServiceTextField($return, 'faxNumber', $attributes, $fields,
$readOnlyFields, 'telephone', $this->messages['facsimileTelephoneNumber'][0], null, $readOnlyFields, 'telephone', $this->messages['facsimileTelephoneNumber'][0], null,
'facsimileTelephoneNumber'); 'facsimileTelephoneNumber');
$this->checkSimpleSelfServiceTextField($return, 'mobile', $attributes, $fields, $this->checkMultiValueSelfServiceTextField($return, 'mobile', $attributes, $fields,
$readOnlyFields, 'telephone', $this->messages['mobile'][0]); $readOnlyFields, 'telephone', $this->messages['mobile'][0]);
$this->checkSimpleSelfServiceTextField($return, 'pager', $attributes, $fields, $this->checkMultiValueSelfServiceTextField($return, 'pager', $attributes, $fields,
$readOnlyFields, 'telephone', $this->messages['pager'][0]); $readOnlyFields, 'telephone', $this->messages['pager'][0]);
$this->checkMultiValueSelfServiceTextField($return, 'street', $attributes, $fields, $this->checkMultiValueSelfServiceTextField($return, 'street', $attributes, $fields,
$readOnlyFields, 'street', $this->messages['street'][0]); $readOnlyFields, 'street', $this->messages['street'][0]);

View file

@ -8,7 +8,7 @@ use LAM\TYPES\ConfiguredType;
/* /*
This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/) This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/)
Copyright (C) 2009 - 2024 Roland Gruber Copyright (C) 2009 - 2025 Roland Gruber
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -159,32 +159,6 @@ class nisnetgroup extends baseModule {
$this->messages['domain'][0] = ['ERROR', _('Domain name'), _('Domain name is invalid!')]; $this->messages['domain'][0] = ['ERROR', _('Domain name'), _('Domain name is invalid!')];
} }
/**
* Returns a list of modifications which have to be made to the LDAP account.
*
* @return array list of modifications
* <br>This function returns an array with 3 entries:
* <br>array( DN1 ('add' => array($attr), 'remove' => array($attr), 'modify' => array($attr)), DN2 .... )
* <br>DN is the DN to change. It may be possible to change several DNs (e.g. create a new user and add him to some groups via attribute memberUid)
* <br>"add" are attributes which have to be added to LDAP entry
* <br>"remove" are attributes which have to be removed from LDAP entry
* <br>"modify" are attributes which have to been modified in LDAP entry
* <br>"info" are values with informational value (e.g. to be used later by pre/postModify actions)
*/
function save_attributes() {
$return = $this->getAccountContainer()->save_module_attributes($this->attributes, $this->orig);
// nisNetgroupTriple needs special changing
if (isset($return[$this->getAccountContainer()->dn_orig]['remove']['nisNetgroupTriple'])) {
$return[$this->getAccountContainer()->dn_orig]['modify']['nisNetgroupTriple'] = $this->attributes['nisNetgroupTriple'];
unset($return[$this->getAccountContainer()->dn_orig]['remove']['nisNetgroupTriple']);
}
if (isset($return[$this->getAccountContainer()->dn_orig]['add']['nisNetgroupTriple'])) {
$return[$this->getAccountContainer()->dn_orig]['modify']['nisNetgroupTriple'] = $this->attributes['nisNetgroupTriple'];
unset($return[$this->getAccountContainer()->dn_orig]['add']['nisNetgroupTriple']);
}
return $return;
}
/** /**
* Returns the HTML meta data for the main account page. * Returns the HTML meta data for the main account page.
* *

View file

@ -1382,10 +1382,13 @@ class posixAccount extends baseModule implements passwordService, AccountStatusP
/** /**
* Returns if groups with same name can be created. * Returns if groups with same name can be created.
* *
* @param ?string $typeId type ID
* @return bool creation is possible * @return bool creation is possible
*/ */
private function allowsToCreateGroupWithSameName(): bool { private function allowsToCreateGroupWithSameName(?string $typeId = null): bool {
$typeId = $this->getAccountContainer()->get_type()->getId(); if ($typeId === null) {
$typeId = $this->getAccountContainer()->get_type()->getId();
}
if (($this->get_scope() !== 'user') if (($this->get_scope() !== 'user')
|| $this->isBooleanConfigOptionSet('posixAccount_' . $typeId . '_hideCreateGroup')) { || $this->isBooleanConfigOptionSet('posixAccount_' . $typeId . '_hideCreateGroup')) {
return false; return false;
@ -2174,7 +2177,7 @@ class posixAccount extends baseModule implements passwordService, AccountStatusP
if ($this->get_scope() == 'user') { if ($this->get_scope() == 'user') {
// primary Unix group // primary Unix group
$primaryGroups = $groups; $primaryGroups = $groups;
$allowToCreateGroupWithUserName = $this->allowsToCreateGroupWithSameName(); $allowToCreateGroupWithUserName = $this->allowsToCreateGroupWithSameName($typeId);
if ($allowToCreateGroupWithUserName) { if ($allowToCreateGroupWithUserName) {
$primaryGroups = [_('Create group with same name') => self::CREATE_GROUP_WITH_SAME_NAME] + $primaryGroups; $primaryGroups = [_('Create group with same name') => self::CREATE_GROUP_WITH_SAME_NAME] + $primaryGroups;
} }

327
lam/lib/modules/takUser.inc Normal file
View file

@ -0,0 +1,327 @@
<?php
/*
This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/)
Copyright (C) 2025 Mark Halliday
2025 Roland Gruber
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
use LAM\TYPES\ConfiguredType;
/**
* Manages the object class "takUser" for users.
*
* @package modules
*
* @author Mark Halliday
* @author Roland Gruber
*/
class takUser extends baseModule {
/**
* Returns true if this module can manage accounts of the current type, otherwise false.
*
* @return boolean true if module fits
*/
public function can_manage() {
return $this->get_scope() === 'user';
}
/** possible roles */
private const ROLE_TYPES = ['Team Member', 'Team Leader', 'HQ', 'RTO', 'K9', 'Medic', 'Forward Observer', 'Sniper'];
/** possible colors */
private const COLOR_TYPES = ['Blue', 'Brown', 'Cyan', 'Dark Blue', 'Dark Green', 'Green', 'Magenta', 'Maroon', 'Orange', 'Purple', 'Red', 'Teal', 'White', 'Yellow'];
/**
* {@inheritDoc}
*/
public function get_metaData() {
$return = [];
// icon
$return['icon'] = 'tak.png';
// alias name
$return["alias"] = _("TAK");
// LDAP filter
$return["ldap_filter"] = ['or' => "(objectClass=takUser)"];
// RDN attribute
$return["RDN"] = ["cn" => "normal"];
// module dependencies
$return['dependencies'] = ['depends' => [], 'conflicts' => []];
// managed object classes
$return['objectClasses'] = ['takUser'];
// managed attributes
$return['attributes'] = ['takcallsign', 'takrole', 'takcolor'
];
// help Entries
$return['help'] = [
'takcallsign' => [
"Headline" => _("Callsign"), 'attr' => 'takCallsign',
"Text" => _("The user's callsign to be displayed to other TAK users. It must be unique.")
],
'takrole' => [
"Headline" => _("Team role"), 'attr' => 'takRole',
"Text" => _("The user's role to be displayed to other TAK users.")
],
'takcolor' => [
"Headline" => _("Team color"), 'attr' => 'takColor',
"Text" => _("The user's team color to be displayed to other TAK users.")
],
];
// upload fields
$return['upload_columns'] = [
[
'name' => 'takuser_takcallsign',
'description' => _('Callsign'),
'help' => 'takcallsign',
'example' => 'UK-ORG-01',
'required' => true,
'unique' => true
],
[
'name' => 'takuser_takrole',
'description' => _('Team role'),
'help' => 'takrole',
'default' => 'Team Member',
'values' => implode(", ", self::ROLE_TYPES),
'example' => 'Team Member',
'required' => true
],
[
'name' => 'takuser_takcolor',
'description' => _('Team color'),
'help' => 'takcolor',
'default' => 'Cyan',
'values' => implode(", ", self::COLOR_TYPES),
'example' => 'Cyan',
'required' => true
],
];
// available PDF fields
$return['PDF_fields'] = [
'takcallsign' => _('Callsign'),
'takrole' => _('Team role'),
'takcolor' => _('Team color'),
];
// profile options
$profileContainer = new htmlResponsiveRow();
$profileContainer->add(new htmlResponsiveInputField(_('Callsign'), 'takuser_takcallsign', null, 'takusercallsign'));
$profileContainer->add(new htmlResponsiveInputField(_('Team Role'), 'takuser_takrole', null, 'takuserrole'));
$profileContainer->add(new htmlResponsiveInputField(_('Team Color'), 'takuser_takcolor', null, 'takusercolor'));
$return['profile_options'] = $profileContainer;
// self-service field settings
$return['selfServiceFieldSettings'] = [
'takcallsign' => _('Callsign'),
'takrole' => _('Team role'),
'takcolor' => _('Team color'),
];
return $return;
}
/**
* {@inheritDoc}
*/
public function load_Messages() {
$this->messages['takcallsign'][0] = ['ERROR', _('Callsign'), _('Callsign contains invalid characters. Valid characters are: a-z, A-Z, 0-9 and .-_')];
$this->messages['takcallsign'][1] = ['ERROR', _('A TAK login with this callsign already exists. Please choose a different callsign.')];
$this->messages['takcallsign'][2] = ['ERROR', _('Callsign'), _('This field is required.')];
$this->messages['takcallsign'][3] = ['ERROR', _('Account %s:') . ' takuser_takcallsign', _('Callsign contains invalid characters. Valid characters are: a-z, A-Z, 0-9 and .-_')];
$this->messages['takrole'][0] = ['ERROR', _('Account %s:') . ' takuser_takrole', _('Please enter a valid option:') . ' ' . implode(', ', self::ROLE_TYPES)];
$this->messages['takcolor'][0] = ['ERROR', _('Account %s:') . ' takuser_takcolor', _('Please enter a valid option:') . ' ' . implode(', ', self::COLOR_TYPES)];
}
/**
* {@inheritDoc}
*/
public function display_html_attributes() {
$return = new htmlResponsiveRow();
// takCallsign
$this->addSimpleInputTextField($return, 'takcallsign', _('Callsign'), true);
// takRole
$takRole = $this->attributes['takrole'][0] ?? '';
$return->add(new htmlResponsiveSelect('takrole', self::ROLE_TYPES, [$takRole], _('Team role'), 'takrole'));
//takColor
$takColor = $this->attributes['takcolor'][0] ?? 'Cyan';
$return->add(new htmlResponsiveSelect('takcolor', self::COLOR_TYPES, [$takColor], _('Team color'), 'takcolor'));
return $return;
}
/**
* {@inheritDoc}
*/
public function process_attributes() {
$return = [];
$this->attributes['takcallsign'][0] = $_POST['takcallsign'];
$this->attributes['takrole'][0] = $_POST['takrole'];
$this->attributes['takcolor'][0] = $_POST['takcolor'];
// check if callsign is filled
if ($_POST['takcallsign'] == '') {
$return[] = $this->messages['takcallsign'][2];
}
elseif (!get_preg($_POST['takcallsign'], 'username')) {
$return[] = $this->messages['takcallsign'][0];
}
// check if callsign is unique
elseif (empty($this->orig['takcallsign'][0]) || ($this->attributes['takcallsign'][0] !== $this->orig['takcallsign'][0])) {
$suffix = $this->getAccountContainer()->get_type()->getSuffix();
$search = searchLDAP($suffix, 'takcallsign=' . $_POST['takcallsign'], ['dn']);
if (!empty($search)) {
$return[] = $this->messages['takcallsign'][1];
}
}
return $return;
}
/**
* {@inheritDoc}
* @see baseModule::build_uploadAccounts()
*/
public function build_uploadAccounts($rawAccounts, $ids, &$partialAccounts, $selectedModules, &$type) {
$errors = [];
for ($i = 0; $i < count($rawAccounts); $i++) {
// add object class
if (!in_array('takUser', $partialAccounts[$i]['objectClass'])) {
$partialAccounts[$i]['objectClass'][] = 'takUser';
}
$this->mapSimpleUploadField($rawAccounts, $ids, $partialAccounts, $i, 'takuser_takcallsign', 'takcallsign', 'username', $this->messages['takcallsign'][3], $errors);
$this->mapSimpleUploadField($rawAccounts, $ids, $partialAccounts, $i, 'takuser_takrole', 'takrole', null, null, $errors);
if (!in_array($partialAccounts[$i]['takrole'][0], self::ROLE_TYPES)) {
$errors[] = array_merge($this->messages['takrole'][0], [[$i]]);
}
$this->mapSimpleUploadField($rawAccounts, $ids, $partialAccounts, $i, 'takuser_takcolor', 'takcolor', null, null, $errors);
if (!in_array($partialAccounts[$i]['takcolor'][0], self::COLOR_TYPES)) {
$errors[] = array_merge($this->messages['takcolor'][0], [[$i]]);
}
}
return $errors;
}
/**
* {@inheritDoc}
* @see baseModule::get_pdfEntries()
*/
public function get_pdfEntries($pdfKeys, $typeId) {
$return = [];
$this->addSimplePDFField($return, 'takcallsign', _('Callsign'));
$this->addSimplePDFField($return, 'takrole', _('Team role'));
$this->addSimplePDFField($return, 'takcolor', _('Team color'));
return $return;
}
/**
* @inheritDoc
*/
public function load_profile($profile) {
// profile mappings in meta data
parent::load_profile($profile);
// special profile options
if (!empty($profile['takuser_takcallsign'][0])) {
$this->attributes['takcallsign'][0] = $profile['takuser_takcallsign'][0];
}
if (!empty($profile['takuser_takrole'][0])) {
$this->attributes['takrole'][0] = $profile['takuser_takrole'][0];
}
if (!empty($profile['takuser_takcolor'][0])) {
$this->attributes['takcolor'][0] = $profile['takuser_takcolor'][0];
}
}
/**
* @inheritDoc
*/
public function getSelfServiceOptions($fields, $attributes, $passwordChangeOnly, $readOnlyFields) {
$return = [];
if ($passwordChangeOnly) {
return $return; // only password fields as long no LDAP content can be read
}
if (!in_array_ignore_case('takUser', $attributes['objectClass'])) {
return $return;
}
$this->addSimpleSelfServiceTextField($return, 'takcallsign', _('Callsign'), $fields, $attributes, $readOnlyFields, true);
if (in_array('takrole', $fields)) {
$role = '';
if (isset($attributes['takrole'][0])) {
$role = $attributes['takrole'][0];
}
if (in_array('takrole', $readOnlyFields)) {
$field = new htmlOutputText($role);
}
else {
$selected = [];
if (!empty($attributes['takrole'][0])) {
$selected = [$attributes['takrole'][0]];
}
$field = new htmlSelect('takUser_takrole', self::ROLE_TYPES, $selected);
}
$return['takrole'] = new htmlResponsiveRow(
new htmlLabel('takUser_takrole', $this->getSelfServiceLabel('takrole', _('Team role'))), $field
);
}
if (in_array('takcolor', $fields)) {
$color = '';
if (isset($attributes['takcolor'][0])) {
$color = $attributes['takcolor'][0];
}
if (in_array('takcolor', $readOnlyFields)) {
$field = new htmlOutputText($color);
}
else {
$selected = [];
if (!empty($attributes['takcolor'][0])) {
$selected = [$attributes['takcolor'][0]];
}
$field = new htmlSelect('takUser_takcolor', self::COLOR_TYPES, $selected);
}
$return['takcolor'] = new htmlResponsiveRow(
new htmlLabel('takUser_takcolor', $this->getSelfServiceLabel('takcolor', _('Team color'))), $field
);
}
return $return;
}
/**
* @inheritDoc
*/
public function checkSelfServiceOptions($fields, $attributes, $passwordChangeOnly, $readOnlyFields) {
$return = ['messages' => [], 'add' => [], 'del' => [], 'mod' => [], 'info' => []];
if ($passwordChangeOnly) {
return $return; // skip processing if only a password change is done
}
if (!in_array_ignore_case('takUser', $attributes['objectClass'])) {
return $return;
}
$this->checkSimpleSelfServiceTextField($return, 'takcallsign', $attributes, $fields, $readOnlyFields, 'username', $this->messages['takcallsign'][0], $this->messages['takcallsign'][2]);
$this->checkSimpleSelfServiceTextField($return, 'takrole', $attributes, $fields, $readOnlyFields);
$this->checkSimpleSelfServiceTextField($return, 'takcolor', $attributes, $fields, $readOnlyFields);
return $return;
}
/**
* @inheritDoc
*/
public function getListAttributeDescriptions(ConfiguredType $type): array {
return [
"takcallsign" => _("Callsign"),
"takrole" => _("Team role"),
"takcolor" => _("Team color"),
];
}
}

View file

@ -13,7 +13,7 @@ use LamTemporaryFilesManager;
/* /*
This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/) This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/)
Copyright (C) 2004 - 2024 Roland Gruber Copyright (C) 2004 - 2025 Roland Gruber
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -155,7 +155,7 @@ if ($_FILES['inputfile'] && ($_FILES['inputfile']['size'] > 0)) {
$checkcolumns = []; $checkcolumns = [];
$columns = []; $columns = [];
foreach ($uploadColumns as $uploadColumn) { foreach ($uploadColumns as $uploadColumn) {
$columns = array_merge($columns, $uploadColumns); $columns = array_merge($columns, $uploadColumn);
} }
foreach ($columns as $column) { foreach ($columns as $column) {
if (isset($column['required']) && ($column['required'] === true)) { if (isset($column['required']) && ($column['required'] === true)) {

View file

@ -2,7 +2,7 @@
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
/* /*
This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/) This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/)
Copyright (C) 2018 - 2024 Roland Gruber Copyright (C) 2018 - 2025 Roland Gruber
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -325,4 +325,13 @@ class AccountTest extends TestCase {
$this->assertFalse(get_preg('ab?c:80', 'hostAndPort')); $this->assertFalse(get_preg('ab?c:80', 'hostAndPort'));
} }
function testAreArrayContentsEqual() {
$this->assertTrue(areArrayContentsEqual([], []));
$this->assertFalse(areArrayContentsEqual(['1'], []));
$this->assertFalse(areArrayContentsEqual([], ['1']));
$this->assertTrue(areArrayContentsEqual(['a', 'b', 'c'], ['c', 'b', 'a']));
$this->assertFalse(areArrayContentsEqual(['a', 'b', 'c'], ['a', 'c', 'd']));
$this->assertFalse(areArrayContentsEqual(['a', 'b', 'c'], ['a', 'c']));
}
} }

View file

@ -27,7 +27,6 @@ parameters:
- '#Call to an undefined method object::.*#' - '#Call to an undefined method object::.*#'
- '#Parameter \#2 \$string of function explode expects string, .* given.#' - '#Parameter \#2 \$string of function explode expects string, .* given.#'
- '#Parameter \#2 \$result of function ldap_.* expects LDAP\\Result, array\|LDAP\\Result given.#' - '#Parameter \#2 \$result of function ldap_.* expects LDAP\\Result, array\|LDAP\\Result given.#'
- '#Cannot access an offset on mixed.#'
- '#Cannot access offset .* on mixed.#' - '#Cannot access offset .* on mixed.#'
- '#Cannot access offset .* on array\|int.#' - '#Cannot access offset .* on array\|int.#'
- '#Cannot access an offset on array\|Countable.#' - '#Cannot access an offset on array\|Countable.#'
@ -47,4 +46,3 @@ parameters:
- '#Binary operation .* between .* and .* results in an error.#' - '#Binary operation .* between .* and .* results in an error.#'
- '#Parameter \#. .* of (function|method) .* expects .*, mixed given.#' - '#Parameter \#. .* of (function|method) .* expects .*, mixed given.#'
- '#Cannot access property .* on mixed#' - '#Cannot access property .* on mixed#'
- '#Cannot use \+\+ on mixed.#'