LDAP 설정
From LimeSurvey Manual
일반
이 기능을 사용하려면 config.php에서 LDAP 지원을 활성화하고 config/ldap.php에서 LDAP 매개변수를 설정해야 합니다.
config.php에서 LDAP 활성화
- $enableLdap: LimeSurvey에서 LDAP 기능을 사용하려면 이 매개변수를 true로 설정해야 합니다(기본값은 false입니다):
'config'=>array( 'debug'=>0, 'debugsql'=>0, 'enableLdap'=>true, )
LDAP 서버 정의
먼저 "application/config/ldap.php"에서 LDAP 서버 연결 옵션을 정의합니다. 각 서버에는 다음 옵션을 사용할 수 있습니다:
- $serverId: 이 LDAP 서버를 식별하는 정수입니다. 쿼리 정의에서 서버를 특정 쿼리에 연결하는 데 사용됩니다;
- $ldap_server[$serverId]['server']: LDAP 서버의 IP 주소 또는 DNS 이름입니다. SSL 보안 연결(LDAPs 또는 LDAP+Start-TLS)을 사용하는 경우 이 이름은 서버 인증서의 CN(또는 SubjectAlternativeName)과 일치해야 합니다;
- $ldap_server[$serverId]['port']: LDAP 서버가 수신 대기하는 TCP 포트입니다. 일반적으로 LDAP 및 LDAP+Start-TLS는 389, LDAPs는 636입니다;
- $ldap_server[$serverId]['protoversion']: 서버가 지원하는 프로토콜에 따라 'ldapv2' 또는 'ldapv3'을 사용할 수 있습니다. 'ldapv3'이 권장 프로토콜입니다. 다만 암호화된 연결을 사용하려면 LDAPs는 'ldapv2' 모드에서 지원되고, Start-TLS는 'ldapv3'의 암호화 방식임에 유의하십시오;
- $ldap_server[$serverId]['encrypt']: 사용할 암호화 방식을 정의합니다. 'ldaps'는 'ldapv2' 서버에서, 'start-tls'는 'ldapv3' 서버에서 지원됩니다. 'none' 키워드는 암호화되지 않은 LDAP 통신에 사용됩니다;
- 'ldaps' 또는 'start-tls' 암호화를 사용하려면 웹 서버가 LDAP 서버의 인증서를 확인할 수 있어야 합니다. 따라서 openldap 라이브러리에서 인증 기관(Certificate Authority)을 정의해야 합니다(리눅스에서는 보통 /etc/openldap/ldap.conf 파일에서 설정합니다).
- $ldap_server[$serverId]['referrals']: 참조(referral)를 따를지 여부를 정의하는 불리언 매개변수입니다(ActiveDirectory의 경우 false를 사용하십시오);
- $ldap_server[$serverId]['encoding']: LDAP 디렉터리가 문자열을 저장할 때 사용하는 인코딩을 지정하는 선택적 매개변수입니다. 기본 인코딩인 'utf-8'이 LDAP 디렉터리의 표준 인코딩이므로 일반적으로 이 매개변수를 설정할 필요는 없습니다. 그러나 Active Directory를 사용하면서 악센트가 있는 문자열을 가져오는 데 문제가 있다면 사용 중인 지역의 인코딩(예: 서유럽은 'cp850')으로 이 매개변수를 설정해 보십시오. 지원되는 전체 인코딩 목록은 CSV 파일에서 토큰 가져오기 화면의 "파일의 문자 세트 선택" 드롭-다운 목록에서 확인할 수 있습니다.
다음으로 디렉터리에 접근하는 데 필요한 인증을 정의해야 합니다. 'anonymous' 접근이 허용되는 경우 다음 두 매개변수를 설정하지 마십시오. 그렇지 않으면 알맞게 설정하십시오:
- $ldap_server[$serverId]['binddn']: 디렉터리를 읽을 수 있는 'LDAP' 사용자의 DN입니다;
- $ldap_server[$serverId]['bindpw']: 위 LDAP 사용자의 비밀번호입니다.
다른 LDAP 서버를 정의해야 하는 경우 다음 줄을 추가하여 serverID를 증가시킨 후 새 매개변수를 정의하십시오:
- $serverId++.
config/ldap.php에서 쿼리 정의
주의: 이 매개변수에 ldap 속성 이름이 필요한 경우 반드시 소문자 이름만 사용하십시오. 예를 들어 displayName이 아니라 displayname을 사용해야 합니다.
샘플 설정이 포함되어 있으므로 config/ldap.php 파일을 참조하십시오.
단순 쿼리
먼저 단순 쿼리부터 살펴보겠습니다. 이 쿼리는 LDAP 항목 자체의 속성과 위치만을 기준으로 필터링합니다. ActiveDirectory를 쿼리할 때는 보통 이것으로 충분합니다.
- $query_id: LDAP 쿼리의 ID입니다;
- $ldap_queries[$query_id]['ldapServerId']: 쿼리를 특정 서버에 연결합니다;
- $ldap_queries[$query_id]['name']: 쿼리를 설명하는 문자열입니다. GUI에 표시됩니다;
- $ldap_queries[$query_id]['userbase']: 사용자 검색에 사용할 루트 DN입니다;
- $ldap_queries[$query_id]['userfilter']: 잠재적인 사용자 항목을 선택하는 데 사용하는 필터입니다. 괄호로 묶어야 합니다;
- $ldap_queries[$query_id]['userscope']: 사용자에 대한 LDAP 검색 범위입니다('base', 'one' 또는 'sub');
- $ldap_queries[$query_id]['firstname_attr']: 토큰 항목의 이름(Firstname) 필드에 매핑될 Ldap 속성입니다;
- $ldap_queries[$query_id]['lastname_attr']: 토큰 항목의 성(Lastname) 필드에 매핑될 Ldap 속성입니다;
- $ldap_queries[$query_id]['email_attr']: 토큰 항목의 이메일 주소 필드에 매핑될 Ldap 속성입니다.
선택적으로 디렉터리에서 더 많은 정보를 가져올 수 있습니다:
- $ldap_queries[$query_id]['token_attr']: 토큰 코드에 매핑될 Ldap 속성입니다;
- $ldap_queries[$query_id]['language']: 사용자 언어 코드에 매핑될 Ldap 속성입니다;
- $ldap_queries[$query_id]['attr1']: attribute_1 필드에 매핑될 Ldap 속성입니다;
- $ldap_queries[$query_id]['attr2']: attribute_2 필드에 매핑될 Ldap 속성입니다.
DN 멤버를 사용하는 결합 그룹 쿼리
이제 좀 더 복잡한 쿼리를 정의하는 방법을 살펴보겠습니다.
다음 쿼리는 먼저 LDAP 그룹을 검색합니다. LDAP 그룹은 다음 형태로 사용자 항목에 대한 참조를 포함하는 LDAP 항목입니다:
- 사용자 ID (예: posixGroups) ==> 다음 섹션 참조
- 또는 사용자 DN (예: groupofnames 및 groupofuniquenames) ==> 아래 참조
여기서는 사용자 DN을 포함하는 그룹을 다룹니다:
- 위에서 설명한 대로 $query_id, $ldap_queries[$query_id]['ldapServerId'], $ldap_queries[$query_id]['name']을 정의합니다.
그런 다음 그룹 필터 매개변수를 정의합니다:
- $ldap_queries[$query_id]['groupbase']: 그룹 항목 검색을 시작할 루트 DN입니다;
- $ldap_queries[$query_id]['groupfilter']: 잠재적인 그룹 항목을 선택하는 LDAP 필터입니다;
- $ldap_queries[$query_id]['groupscope']: 그룹에 대한 LDAP 검색 범위입니다('one', 'base' 또는 'sub');
- $ldap_queries[$query_id]['groupmemberattr']: 사용자 항목에 대한 참조를 포함하는 그룹 항목의 LDAP 속성 이름입니다;
- $ldap_queries[$query_id]['groupmemberisdn']: TRUE.
이 시점에서 첫 번째 LDAP 검색이 선택한 그룹에 해당하는 사용자를 찾을 수 있도록 모든 설정이 끝났습니다. 다만 이러한 "사용자 후보"에 다른 필터를 적용하여 선택 대상을 제한할 수도 있습니다. 물론 이는 선택 사항입니다:
- $ldap_queries[$query_id]['userbase']: 사용자 LDAP 검색의 기준 DN입니다(이 기준과 일치하는 사용자 후보만 선택됩니다);
- $ldap_queries[$query_id]['userscope']: 사용자 LDAP 검색의 범위입니다(userbase+scope와 일치하는 사용자 후보만 선택됩니다);
- $ldap_queries[$query_id]['userfilter']: 각 사용자 후보 항목(의 속성)에 적용되어 추가 선택 조건을 부여하는 필터입니다.
UID 멤버를 사용하는 결합 그룹 쿼리
이제 그룹 멤버가 사용자 DN이 아니라 사용자 UID인 경우 결합 그룹 쿼리를 정의하는 방법을 살펴보겠습니다.
DN 멤버를 사용하는 그룹 쿼리와 마찬가지로, 이 쿼리도 먼저 LDAP 그룹 항목을 검색하여 멤버를 가져옵니다. 이렇게 얻은 멤버 값은 해당 항목을 찾기 위한 사용자 검색 필터에 사용됩니다. 따라서 그룹에서 찾은 멤버 UID와 일치해야 하는 사용자 항목의 속성을 정의하는 매개변수를 추가로 설정해야 합니다.
필수 매개변수를 살펴보겠습니다:
- 위에서 설명한 대로 $query_id, $ldap_queries[$query_id]['ldapServerId'], $ldap_queries[$query_id]['name']을 정의합니다.
그런 다음 그룹 필터 매개변수를 정의합니다:
- $ldap_queries[$query_id]['groupbase']: 그룹 항목 검색을 시작할 루트 DN입니다;
- $ldap_queries[$query_id]['groupfilter']: 잠재적인 그룹 항목을 선택하는 LDAP 필터입니다;
- $ldap_queries[$query_id]['groupscope']: 그룹에 대한 LDAP 검색 범위입니다('one', 'base' 또는 'sub');
- $ldap_queries[$query_id]['groupmemberattr']: 사용자 항목에 대한 참조를 포함하는 그룹 항목의 LDAP 속성 이름입니다;
- $ldap_queries[$query_id]['groupmemberisdn']: FALSE;
- $ldap_queries[$query_id]['useridattr']: 그룹 멤버에서 찾은 UID와 일치해야 하는 사용자 속성의 이름입니다.
이 시점에서 첫 번째 LDAP 검색이 선택한 그룹에 해당하는 사용자 UID를 찾을 수 있도록 모든 설정이 끝났으며, 사용자 검색 필터는 자동으로 채워집니다.
다만 구성원 UID에서 자동으로 계산된 사용자 필터를 보완하여, 어떤 '사용자 후보'를 선택할지 제한할 수 있습니다. 이 설정은 물론 선택 사항입니다:
- $ldap_queries[$query_id]['userbase']: 사용자 LDAP 검색의 기본 DN입니다(이 기본 DN에 일치하는 사용자 후보만 선택됩니다);
- $ldap_queries[$query_id]['userscope']: 사용자 LDAP 검색의 범위입니다(userbase+scope에 일치하는 사용자 후보만 선택됩니다);
- $ldap_queries[$query_id]['userfilter']: 각 사용자 후보 항목에(해당 항목의 속성에 대해) 적용되어 추가 선택 조건을 부여하는 필터입니다.
Active Directory는 어떻게 하나요?
Active Directory(AD)는 LDAP 프로토콜을 사용하여 쿼리할 수 있는 Microsoft의 레지스트리입니다.
따라서 AD의 내용을 LimeSurvey 토큰 쿼리에 사용할 수 있지만, 이를 위해서는 AD가 어떻게 구성되어 있는지 알고 있어야 합니다.
- LDAP 루트 기본 DN은 dc=my_windows_domain_name,dc=dns_suffix2,dc=dns_suffix1 입니다
==> 예를 들어, 회사가 DNS 도메인 'my-company.com'을 소유하고 Windows 도메인이 'employees'라면 루트 기본 DN은 dc=employees,dc=my-company,dc=com 입니다.
- 사용자와 사용자 그룹은 cn=Users,dc=my_windows_domain_name,dc=dns_suffix2,dc=dns_suffix1 아래에 저장됩니다(ou=users가 아닌 점에 유의하십시오);
- Active Directory 그룹:
- 그룹 개체는 'member' 속성에 구성원의 DN을 포함합니다;
- 그룹 멤버십은 각 사용자 항목의 memberOf 속성에도 저장됩니다. 이 속성에는 사용자가 속한 그룹의 DN이 들어 있습니다;
- 일부 그룹은 CN=Builtin,dc=my_windows_domain_name,dc=dns_suffix2,dc=dns_suffix1 에 있습니다:
- 예: cn=Administrator,CN=Builtin,dc=my_windows_domain_name,dc=dns_suffix2,dc=dns_suffix1;
경우에 따라 Active Directory를 쿼리하는 것이 쉽지 않을 수 있으므로, 다음은 Active Directory에서 일부 정보를 가져오는 샘플 구성입니다:
//Connection to the active directory Server:
$serverId=0;
$ldap_server[$serverId]['server'] = "10.10.10.10";
$ldap_server[$serverId]['port'] = "389";
$ldap_server[$serverId]['protoversion'] = "ldapv2";
$ldap_server[$serverId]['encrypt'] = "none"; // Most AD LDAP servers will not have encryption set by default
$ldap_server[$serverId]['referrals'] = false;
$ldap_server[$serverId]['binddn'] = "domain\\user";
$ldap_server[$serverId]['bindpw'] = "userpassword";
//$ldap_server[$serverId]['binddn'] = "CN=user,OU=user_group,DC=xxx,DC=yyy"; this one will not work with active directory, that´s why you need to use "domain\\user"
//Here is a sample query for getting all active users of an active directory:
$query_id=0;
$ldap_queries[$query_id]['ldapServerId'] = 0;
$ldap_queries[$query_id]['name'] = 'Staff with an enabled account';
$ldap_queries[$query_id]['userbase'] = 'OU=USER_GROUP,DC=xxx,DC=yyy';
$ldap_queries[$query_id]['userfilter'] = '(&(objectClass=user)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))';
//AD has no "account is enabled" attribute, so the filter excludes accounts that have the "disabled" flag (bit 2) set in userAccountControl
$ldap_queries[$query_id]['userscope'] = 'sub';
$ldap_queries[$query_id]['firstname_attr'] = 'givenname';
$ldap_queries[$query_id]['lastname_attr'] = 'sn';
$ldap_queries[$query_id]['email_attr'] = 'mail';
$ldap_queries[$query_id]['token_attr'] = ''; // Leave empty for Auto Token generation by phpsv
$ldap_queries[$query_id]['language'] = '';
$ldap_queries[$query_id]['attr1'] = '';
$ldap_queries[$query_id]['attr2'] = '';
//You can also filter on group membership through the memberOf attribute of the user. Here is a sample query for getting all active users that are member of the group "samplegroup" in active directory:
$query_id++;
$ldap_queries[$query_id]['ldapServerId'] = 0;
$ldap_queries[$query_id]['name'] = 'All members of samplegroup';
$ldap_queries[$query_id]['userbase'] = 'OU=USER_GROUP,DC=xxx,DC=yyy';
$ldap_queries[$query_id]['userfilter'] = '(&(objectClass=user)(memberOf=CN=samplegroup,OU=Group Global,OU=USER_GROUP,DC=xxx,DC=yyy)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))';
$ldap_queries[$query_id]['userscope'] = 'sub';
$ldap_queries[$query_id]['firstname_attr'] = 'givenname';
$ldap_queries[$query_id]['lastname_attr'] = 'sn';
$ldap_queries[$query_id]['email_attr'] = 'mail';
$ldap_queries[$query_id]['token_attr'] = ''; // Leave empty for Auto Token generation by phpsv
$ldap_queries[$query_id]['language'] = '';
$ldap_queries[$query_id]['attr1'] = '';
$ldap_queries[$query_id]['attr2'] = '';
사용자 쿼리의 또 다른 예:
$ldap_queries[$query_id]['userfilter'] = '(&(objectCategory=Person)(objectClass=user)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))'; // AD doesn't recognise enabled accounts in the normal way, so instead, we check users are not disabled
- 이메일 주소가 없는 사용자를 건너뛰려면 사용자 필터에 (mail=*)를 추가하는 것을 고려해 보십시오.
그룹 쿼리 예:
$ldap_queries[$query_id]['groupfilter'] = '(&(objectClass=group)(cn=Domain Admins))'; // AD doesn't use the standard attribute name for groups, so use this example instead.
Active Directory 그룹별 쿼리를 자동으로 생성하기
설문조사 참여자 그룹을 하나의 조직 단위(OU)에 보관하고 있다면, 그룹마다 쿼리를 작성할 필요가 없습니다. 다음 코드는 해당 OU의 모든 그룹을 목록으로 가져와 그룹마다 하나의 쿼리를 생성합니다. 이 쿼리에는 중첩된 그룹의 구성원을 포함하여 해당 그룹의 사용 가능한 모든 구성원이 포함됩니다.
서버 및 쿼리 정의 뒤, //DO NOT CHANGE BELOW HERE 줄 앞에 있는 config/ldap.php에 이 코드를 추가하십시오. 그런 다음 맨 위의 네 가지 변수를 설정하십시오:
- $groupServerId: 사용할 서버의 $serverId;
- $groupContainer: 그룹을 포함하는 OU의 DN입니다. 이 OU에 직접 속한 그룹만 목록에 표시되며, 하위 OU의 그룹은 포함되지 않습니다;
- $groupUserBase: 그룹 멤버를 검색할 루트 DN;
- $groupNameAttr: GUI에서 쿼리 이름으로 사용되는 그룹 속성(소문자)입니다. 예: 'info'("Active Directory 사용자 및 컴퓨터"의 "Notes" 필드) 또는 'description'. 그룹에 이 속성의 값이 없으면 cn이 사용됩니다.
$groupServerId = 0;
$groupContainer = 'OU=Survey groups,DC=xxx,DC=yyy';
$groupUserBase = 'DC=xxx,DC=yyy';
$groupNameAttr = 'info';
$srv = $ldap_server[$groupServerId];
$ds = ldap_connect(($srv['encrypt'] == 'ldaps' ? 'ldaps://' : 'ldap://') . $srv['server'] . ':' . $srv['port']);
if ($ds) {
ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, $srv['protoversion'] == 'ldapv3' ? 3 : 2);
ldap_set_option($ds, LDAP_OPT_REFERRALS, 0);
// Never send the bind password if Start-TLS was requested but failed
$secure = $srv['encrypt'] != 'start-tls' || @ldap_start_tls($ds);
$bound = $secure && (empty($srv['binddn']) ? @ldap_bind($ds) : @ldap_bind($ds, $srv['binddn'], $srv['bindpw']));
$result = $bound ? @ldap_list($ds, $groupContainer, '(objectCategory=group)', array('cn', $groupNameAttr)) : false;
$groups = $result ? ldap_get_entries($ds, $result) : array('count' => 0);
for ($i = 0; $i < $groups['count']; $i++) {
$query_id++;
$ldap_queries[$query_id]['ldapServerId'] = $groupServerId;
$ldap_queries[$query_id]['name'] = isset($groups[$i][$groupNameAttr][0]) ? $groups[$i][$groupNameAttr][0] : $groups[$i]['cn'][0];
$ldap_queries[$query_id]['userbase'] = $groupUserBase;
// Enabled users with an email address that are members of the group, directly or through nested groups
$ldap_queries[$query_id]['userfilter'] = '(&(objectCategory=person)(objectClass=user)(mail=*)'
. '(!(userAccountControl:1.2.840.113556.1.4.803:=2))'
. '(memberOf:1.2.840.113556.1.4.1941:=' . ldap_escape($groups[$i]['dn'], '', LDAP_ESCAPE_FILTER) . '))';
$ldap_queries[$query_id]['userscope'] = 'sub';
$ldap_queries[$query_id]['firstname_attr'] = 'givenname';
$ldap_queries[$query_id]['lastname_attr'] = 'sn';
$ldap_queries[$query_id]['email_attr'] = 'mail';
$ldap_queries[$query_id]['token_attr'] = '';
$ldap_queries[$query_id]['language'] = '';
$ldap_queries[$query_id]['attr1'] = '';
$ldap_queries[$query_id]['attr2'] = '';
}
ldap_unbind($ds);
}
참고 사항:
- 이 코드는 "다음에서 참여자 가져오기" LDAP 페이지를 열거나 제출할 때마다 디렉터리에 연결하므로, 구성을 변경하지 않아도 새 그룹이 나타납니다. 연결 또는 바인드에 실패하면 페이지에는 직접 정의한 쿼리만 표시됩니다.
- 일치 규칙 1.2.840.113556.1.4.1941(LDAP_MATCHING_RULE_IN_CHAIN)은 중첩된 그룹을 확인합니다. 이 규칙은 Active Directory에만 있으므로 이 예제는 다른 LDAP 서버에서는 작동하지 않습니다. 다른 서버에서는 각 그룹마다 결합 그룹 쿼리를 사용하십시오.
- $groupContainer의 하위 OU에 있는 그룹까지 목록에 포함하려면 ldap_list를 ldap_search로 바꾸십시오.
- 둘 이상의 OU에서 그룹을 목록으로 가져오려면, 네 가지 변수에 다른 값을 지정하여 코드를 반복하십시오.
Active Directory LDAP 구조에 대한 자세한 내용은 Active Directory Architecture 및 Active Directory Technical Specification에서 확인하실 수 있습니다.