ECUM 버전 0.0.2b (설치 방법 및 기타 관련 정보)

등록일시: 2003-11-05 14:56,  수정일시: 2018-04-07 11:10
조회수: 28,727
본문은 최초 작성 이후, 약 21년 이상 지난 문서입니다. 일부 내용은 최근의 현실과 맞지 않거나 동떨어져 있을 수 있으며 문서 내용에 오류가 존재할 수도 있습니다. 또한 본문을 작성하던 당시 필자의 의견과 현재의 의견에 많은 차이가 존재할 수도 있습니다. 이 점, 참고하시기 바랍니다.

목차

ECUM 버전 0.0.2b의 개요

이번에 소개드리는 ECUM 버전 0.0.2b 프로그램은 별도의 클라이언트 응용 프로그램을 사용자의 컴퓨터에 설치하지 않고도 단지 인터넷 익스플로러만을 사용해서 윈도우즈 서버에 존재하는 사용자 계정과 그룹을 관리할 수 있는 편리한 기능을 제공해주는 ADSI 기술 기반의 ASP 프로그램이다. 여담으로 필자 역시도 사람인지라 이 ECUM 프로그램이 정말로 좋은 프로그램이고 최신 기술을 사용했으며 완벽하다며 자랑하고 싶은 마음이 앞서지만, 자신에게 조금 더 엄격해져서 여러분들께 진실을 말씀드린다면 그렇지만은 않다는 것이 올바른 평가일 것이다.

무엇보다도 이 사실을 대변해주는 가장 대표적인 사례가 다름 아닌 ECUM 프로그램에 사용된 ADSI 기술 그 자체다. 단적으로 ECUM 프로그램에서 사용되고 있는 모든 ADSI 관련 기술들은 약 2년여 전부터 제공되던 초기의 ADSI 관련 기술과 비교할 때, 단 한 치의 다름도 없이 동일한 것이다. 이는 근본적으로 마이크로소프트사에서 그동안 ADSI를 업데이트하지 않았으므로 어쩌면 당연한 결과일런지도 모르겠다. 그러나, 결론적으로 이 말은 한 2년쯤 전에도 누군가 마음만 먹었었다면 ECUM과 동일한 기능을 가진 프로그램을 제작하는 것이 가능했다는 사실을 반증해 주는 것이다.

그렇다고해서 ADSI는 이제 낡고 오래된 쓸모 없는 기술이라는 성급한 판단을 내리지는 말기를 바란다. 현재 가장 최신의 윈도우즈 계열 운영체제인 윈도우즈 2003 제품군에서도 ADSI는 WMI와 함께 역시 중요한 한 축을 담당하고 있으며, 아직까지도 ADSI가 아니고서는 비주얼 베이직 계열의 개발자가 쉽게 접근할 수 없는 영역이 상당히 많이 남아있는 것이 현실이기 때문이다. 더군다나 .NET 기반의 최신 프로그래밍 환경에서도 여전히 ADSI의 중요성은 간과되지 않고 있다.

그리고, ECUM 프로그램의 또 한 가지 중요한 제약 사항은 현재 버전이 도메인 컨트롤러와 액티브 디렉터리에서 관리되는 사용자 계정과 그룹을 대상으로 한 기능들을 전혀 지원하지 않고 있다는 점이다. 0.0.2b 버전의 모든 기능들은 오로지 ECUM 프로그램이 설치된 윈도우즈 서버 그 자체의 로컬 계정만을 대상으로 구현된 것이다. ECUM 프로그램을 활용해서 새로이 프로그램을 개발하거나 기존의 프로그램에 더하여 추가적인 응용을 하시고자 하는 분들께서는 현재로써는 어쩔 수 없이 해당 부분들에 대한 지원을 직접 구현해야만 한다.

게다가 ADSI 프로그래밍의 전체적인 방향을 결정할 때 가장 큰 영향을 미치는 ADSI 제공자의 선택 또한, LDAP:// 제공자가 아닌 가장 기본적인 기능만을 제공하는 WinNT:// 제공자를 선택했는데, EgoCube.IISWebAdmin 컴포넌트의 경우와 마찮가지로 아직까지 NT 시스템을 운영하고 계시는 분들을 배려하기 위한 선택이었다. 더군다나 필자는 IIS 기반의 웹 서버가 도메인이나 액티브 디렉터리에 묶여서 운영되는 경우가 많지 않다고 생각하므로 현실적으로 그 사용에 큰 제약이 없을 것이라고 판단했기 때문이다.

마지막으로 이 글에서 기준으로 삼고 있는 ECUM 프로그램 0.0.2b 버전은 필자의 홈페이지나 기타 외부 사이트에 공개했었던 초기의 버전에서 일부 오류를 수정하고 몇 가지 오류 메세지를 보강한 가장 최신의 버전이므로 관련된 모든 내용은 묵시적으로 이 0.0.2b 버전을 기준으로 설명하도록 하겠다.

ECUM 버전 0.0.2b 다운로드 및 설치 방법

압축 파일 형식으로 제공되고 있는 ECUM 버전 0.0.2b의 소스 파일은 다음의 링크를 통해서, 또는 언제나처럼 필자의 홈페이지에서 서비스되고 있는 FileCube를 통해서 다운로드 받을 수 있다. 그리고, FileCube에는 가장 최신의 버전인 0.0.2b 버전뿐만 아니라 최초로 공개된 버전인 0.0.2 버전과 0.0.2a 버전도 함께 준비되어 있으므로 참고하기 바란다.


ECUM_ASP_0.0.2b.zip (48k)

다운로드가 성공적으로 끝났다면 설치하고자 하는 가상 웹 서버 하위의 적당한 폴더에 다운로드 받은 파일을 이동한 후 압축을 풀어준다. 이 때 압축이 풀린 프로그램 소스 파일의 설치 위치에는 그 어떠한 제약 조건도 존재하지 않으며, 각각의 폴더들의 상대적인 위치와 파일명 및 폴더명만 동일하게 유지되면 어떤 경로에 설치되더라도 프로그램의 실행에는 영향을 미치지 않는다. 파일의 복사를 모두 마쳤다면 그 외에 별도로 상수값 설정이나 기타 초기화 과정 등의 추가 작업은 일절 필요가 없으므로 그 상태에서 바로 인터넷 익스플로러를 사용하여 ECUM 프로그램을 실행시킬 수 있다. 다음 이미지는 ECUM 버전 0.0.2b 가 정상적으로 실행된 상태의 모습이다.

ECUM 버전 0.0.2b의 실행 상태

때에 따라서는 위의 화면이 바로 나타나는 것이 아니라 일단 로그인 다이얼로그 박스가 나타나서 먼저 인증을 요구하는 경우가 있을 수 있는데, 이는 ECUM 프로그램이 설치된 서버의 위치와 현재 로그인한 사용자 계정의 권한과 관련된 사항으로 잠시 후에 다시 자세히 설명을 하도록 하겠다. 일단 지금은 ECUM 프로그램이 사용자 본인의 컴퓨터에 설치되어 있으며, 원도우즈 로그인은 관리자(Administrators) 그룹에 속한 계정을 사용하여 로그인을 했다고 가정하고서 나머지 글을 진행하도록 하겠다.

ECUM 버전 0.0.2b의 저작권 관련 사항

난데없이 '저작권' 이라는 단어가 나와서 순간적으로 의아하게 생각하시는 분들도 계실 줄로 생각한다. 하지만 필자가 갑자기 저작권에 대해서 별도의 거론을 하기로 마음을 먹게된 이유는 그 사용권을 제한하고 필자가 어떠한 이득을 취하고자 함이 아니라, 필자에게 직접 문의를 해오시는 분들이 가끔 계셔서 스스로 이 자리를 빌어서 공식적인 입장을 투명하게 밝히고 사용자들이 혹시 느낄지도 모를 궁금증을 해소하고자 하는 취지에서이다.

다음의 조항들은 2003년 12월 01일 현재 날짜를 기준으로 지금까지 필자의 홈페이지에서 공개된 모든 프로그램들과 앞으로 공개될 대부분의 프로그램들을 그 대상으로 하며, 필자가 특별히 별도의 명시를 하지 않는 경우 공개되는 모든 프로그램들은 이 조항들을 따른다. 그 내용은 비교적 간단하다.

  • 문서 또는 필자의 홈페이지를 통해서 공식적으로 별도의 명시가 되지 않는 이상, 필자의 홈페이지에서 공개되는 모든 프로그램들은 프리웨어다.
  • 여기서 프리웨어라는 단어의 의미는 모든 사용자가 해당 프로그램의 소스 코드 또는 디자인 등을 포함한 모든 내용을 수정하고 재배포까지 할 수 있음을 의미한다.
  • 저작권 표시나 주석 역시도 마음대로 제거하거나 수정할 수 있으며, 상업적인 사용을 포함한 그 어떠한 종류의 제약도 존재하지 않는다. 그러나, 다음과 같은 단 한 가지 제약 사항을 위반하는 경우에는 그 도의적, 법적 책임을 강력하게 묻는다.
  • 각각의 사용자는 해당 프로그램을 사용하여 제작한 자신의 2차 제작물에 대한 권리를 남용하여 다른 사용자들의 필자의 해당 프로그램에 대한 자유로운 권리를 제한하지 못한다. 즉, 상식적으로 납득하기 어려운 수준에서의 필자의 원본 프로그램과 동일한 내용의 프로그램을 가지고 다른 사용자들이 필자의 프로그램을 자유롭게 사용할 수 있는 권리를 침해할 수 없다.

결국 위 조항들이 전달하고자 하는 핵심 의미는 프로그램 자체는 그 어떠한 용도로도 사용해도 되는 반면, 해당 프로그램에 대한 타인의 권한은 절대 침해할 수 없다는 뜻이다.

각각의 항목들을 읽어보고 어떤 느낌을 받으실지는 모르겠지만 필자는 스스로를 대단한 프로그래머라고는 생각하지 않는다. 또한 필자가 공개한 각각의 프로그램들도 역시 엄청나게 대단한 것이라고는 생각하지 않는다. 그럼에도 불구하고 위와 같은 조항들까지 들먹이면서 이런 얘기를 하는 것은, 보다 많은 분들이 필자의 프로그램을 자유롭게 사용할 수 있었으면 하는 개인적인 바램 때문이다.

ECUM 버전 0.0.2b 적용 기술

개요

이미 글의 시작 부분에서 소개했던 것처럼 ECUM 버전 0.0.2b 프로그램은 인터넷 익스플로러를 사용해서 윈도우즈 서버의 사용자 계정과 그룹의 정보를 관리하는 기능을 제공해주는 ADSI 기술 기반의 ASP 프로그램이다. 그러므로 지극히 당연한 얘기가 되겠지만, ECUM의 모든 기능들은 ADSI가 제공해주는 기능들에 의해서 전적으로 좌우받게 되는데 불행하게도 사용자 계정과 그룹의 정보를 관리하는 기능과 관련하여 ADSI에는 한 가지 치명적인 제약이 존재하고 있다.

그것은 바로 ADSI만으로는 해당 사용자 계정의 보안 식별자(SID, Security Identifier)를 얻을 수 있는 방법이 전무하다는 사실이다. 보안 식별자가 무엇인지 모르는 분들이 계실수도 있으므로 잠시 설명을 하고 넘어가도록 하겠다. 윈도우즈 NT 계열의 시스템에서는 생성되는 모든 사용자 계정마다 각각 하나씩 고유한 보안 식별자가 할당된다. 이 보안 식별자는 'S-1-5-21-35135249072896'와 같은 형태를 가지고 있는 유일한 값으로서 윈도우즈 NT 계열의 시스템은 보안과 관련된 모든 동작에서 계정이 아니라 바로 이 보안 식별자를 판단의 근거로 사용하게 된다. 따라서, 이 보안 식별자의 값이 틀리다면 똑같은 알파벳으로 이루어진 사용자 계정이라고 할지라도 두 계정은 시스템에 의해서 각각 서로 완벽하게 다른 계정으로 취급된다.

그렇다면, 이 보안 식별자를 ADSI를 통해서 얻을 수 없다는 점이 왜 크게 문제가 되는 것일까? 보안 식별자들 중에는 일반적인 경우와는 달리 조금 특별하게 취급되어지는 보안 식별자들이 다수 존재하는데, 이런 보안 식별자들을 친숙한 보안 식별자(Well-Known SIDs)라고 부른다. 가령, Administrator 또는 Guest 같은 특별한 계정들의 보안 식별자들이 바로 이에 해당되는데, 이를테면 Administrator와 Guest 계정의 친숙한 보안 식별자는 각각 'S-1-5-domain-500'과 'S-1-5-domain-501'이다. 참고로 윈도우즈 2000 제품군을 기준으로 작성된 친숙한 보안 식별자의 전체 리스트와 그 상세 정보는 다음의 URL 에서 찾아볼 수 있다.

문제는 이런 보안 식별자들의 값만 그대로 유지된다면 계정 관리자나 사용자가 직접 자신의 계정이나 그룹의 이름을 마음대로 변경할 수가 있다는 점이다. 일반적인 사용자 계정이나 계정 관리자가 운영상의 목적으로 작성한 그룹의 이름을 바꾸는 경우에는 그다지 심각한 문제가 발생하지 않지만, 특수한 목적으로 사용되는 계정이나 그룹, 즉 앞에서 얘기했던 Administrator나 Guest 같은 계정이나 비슷한 목적으로 시스템에 의해서 생성된 그룹, Administrators 그룹이나 Backup Operators 그룹, Users 그룹 등의 이름이 변경되는 경우에는 그 변경된 상태를 제대로 파악할 수가 없다.

그래서, ECUM 프로그램에서는 계정이나 그룹의 이름을 바꿀 수 없도록 인터페이스를 작성해 놓았지만, 여전히 콘솔에서 직접 '로컬 사용자 및 그룹' 스냅인을 통해서 이름을 변경할 수 있으므로 이런 경우에는 올바른 동작이 보장되지 않는다. 예를 들어서, 계정 관리자가 임의로 Users 그룹의 이름을 My Users 그룹으로 변경했다면 근본적으로 윈도우즈 시스템 내부에서는 여전히 친숙한 보안 식별자를 기준으로 현재의 My Users 그룹이 다름아닌 예전의 Users 그룹이라는 사실을 식별할 수 있다. 그러나, ADSI 상에서는 단순히 'Users'라는 문자열 자체만을 가지고 해당 그룹이 시스템에서 관리되는 Users 그룹이라는 사실을 파악할 수 밖에 없으므로, 누군가가 My Users 그룹을 삭제하려고 시도한다고 해도 프로그래밍 차원에서 이를 감지하거나 제어할 수가 없는 것이다.

물론, 이런 친숙한 보안 식별자를 갖고 있는 계정이나 그룹을 삭제하려고 시도하는 경우, ADSI를 사용하는 경우를 포함한 일반적인 방법으로는 오류가 발생하면서 동작이 허용되지 않으므로 시스템에 손상을 입히는 사태가 발생하거나 하지는 않는다. 그러나, 프로그램 사용자의 입장에서는 영문모를 오류 메세지를 받게되어 혼란을 느낄수 있으며 개발자의 입장에서는 프로그램을 완벽하게 컨트롤 할 수 없다는 아쉬움이 남는다. 이처럼 보안 식별자의 값은 ADSI만으로는 얻을 수 없으며 Win32 API나 다른 서비스, 가령 익스체인지 서버에서 제공해주는 관리 개체인 MSExchange.AcctMgmt 개체의 도움을 받는 등 상황에 맞는 적절한 외부 모듈의 지원이 필요하다.

이처럼 ADSI만으로는 친숙한 보안 식별자를 가진 계정이나 그룹을 구분할 수 있는 방법이 없으므로 부족하나마 해결책의 일환으로 ECUM 프로그램 내부적으로 별도의 목록을 관리한다. 이 친숙한 보안 식별자의 목록은 ASP 라이브러리 폴더 하위에 존재하는 ecum_wksi.asp 파일에 위치해 있으며, 일부 중요하거나 자주 사용되는 항목 몇 가지만 등록되어져 있을 뿐, 앞에서 설명한 친숙한 보안 식별자의 전체 항목이 등록되어 있지는 않다. 따라서, 필요한 경우에는 이 파일에 항목을 추가하여 사용해야만 한다.

물론, 이 문제와 관련하여 별도의 컴포넌트를 작성한다던가 기타 다른 확실한 방법을 사용하여 문제점을 해결하는 길도 있었을 것이다. 그러나, 필자가 매번 강조하는 것처럼 필자가 공개하는 모든 프로그램들은 제품이 아니며 해당 프로그램에 관심을 갖게 된 초보자분들이 설치 문제 때문에 정작 프로그램은 살펴보지도 못하고 곤란을 겪게 되는 것을 원하지 않는다. 그래서, 몇몇 특별한 경우를 제외하고는 필자가 외부에 공개할 프로그램을 작성할 때 가장 먼저 전제조건으로 생각하는 사항이 웹 호스팅을 받는 분들이 그 프로그램을 무리 없이 설치할 수가 있느냐 없느냐 하는 부분인 것이다. 물론, ECUM 프로그램의 경우에는 프로그램 특성상 전제조건과 부합되지 않을 수 밖에 없는 부분들이 있지만 기본적인 필자의 마음가짐이 그러하다는 뜻이다.

기본 기능

다음 목록은 ECUM 버전 0.0.2b에서 제공되고 있는 기본적인 기능들의 목록이다. 근본적으로 먼저 공개되었던 초기의 ECUM 버전 0.0.2와 별다른 기능상의 차이는 없으며 다만 몇 가지 오류들이 수정된 것으로, 그 수정된 오류 내역들 중 대부분이 친숙한 보안 식별자 관련 오류들의 메세지 출력과 관련된 부분들이므로 보안 문제와 관련된 부분이라던가 윈도우즈 시스템에 치명적인 영향을 미치는 부분은 조금도 존재하지 않는다. 그러나, 보다 확실한 결과를 원한다면 0.0.2b 버전으로 업그레이드하는 것을 권장한다.

  • 사용자 계정 관리 관련 기능
    • 윈도우즈 서버의 로컬 사용자 계정 목록 출력을 제공하고 출력된 목록의 각각의 컬럼에 대하여 정렬과 필터링이 가능하다.
    • 윈도우즈 서버에 새 로컬 사용자 계정을 생성하여 추가할 수 있다.
    • 윈도우즈 서버의 특정 로컬 사용자 계정의 제한적인 기본 정보를 수정할 수 있으나 '로컬 사용자 및 그룹' 스냅인에서 제공해주는 기능들 중 극히 기본적인 일부 항목들만을 지원한다.
    • 윈도우즈 서버의 특정 로컬 사용자 계정을 삭제할 수 있다. 단, 친숙한 보안 식별자를 가진 특정 계정은 삭제를 시도할 경우 오류가 발생한다. ex) Administrator, Guest
    • 윈도우즈 서버의 특정 로컬 사용자 계정의 암호를 재설정 할 수 있다.
  • 그룹 관리 관련 기능
    • 윈도우즈 서버의 로컬 그룹의 목록 출력을 제공하고 출력된 목록의 각각의 컬럼에 대하여 정렬과 필터링이 가능하다.
    • 윈도우즈 서버에 새 그룹을 생성하여 추가할 수 있다.
    • 윈도우즈 서버의 특정 그룹의 기본 정보를 수정할 수 있다.
    • 해당 윈도우즈 서버의 특정 그룹을 삭제할 수 있다. 단, 친숙한 보안 식별자를 가진 특정 그룹은 삭제를 시도할 경우 오류가 발생한다. ex) Administrators 그룹, Users 그룹, Guests 그룹
    • 윈도우즈 서버의 특정 그룹에 특정 사용자 계정을 새로운 멤버로 추가하거나 현재 해당 그룹의 멤버인 사용자 계정을 멤버 목록에서 제거할 수 있다. 단, 일부 사용자 계정은 특정 그룹에서 제거하는 것이 불가능하다. ex) Administrators 그룹의 Administrator

이 목록에서도 알 수 있는 것처럼 ECUM 버전 0.0.2b에서 제공해주는 모든 기능들은 그 구현의 대상 샘플 모델이라고도 말할 수 있는 '로컬 사용자 및 그룹' 스냅인의 가장 기본적인 기능들만을 모아서 다시 구현한 것이다. 개인적인 판단으로는 ECUM 프로그램은 현재 버전 그대로만으로는 복잡한 조건들이 요구되는 실제 업무에서 사용되기에는 조금 무리가 있다고 생각되고, 아마도 제한적인 단순 업무나 ADSI의 기본적인 기술을 습득하기 위한 샘플 자료로서는 다소나마 유용하게 사용될 수 있지 않을까 생각한다.

그리고, 이 목록에서 설명하고 있는 친숙한 보안 식별자를 가진 특정 사용자 계정이나 특정 그룹을 삭제하려고 시도하거나, 특정 사용자 계정을 특정 그룹의 멤버 목록에서 제거하려고 할 때 발생하는 오류는 프로그램에서 내부적으로 관리되어 다음과 같은 오류 메세지가 출력되는데, 단지 메세지만 출력될 뿐 시스템에는 일체의 악영향을 미치지 않으므로 혹시라도 걱정하지는 말기 바란다.

친숙한 보안식별자와 관련된 오류 메세지

이 이미지는 Administrators 그룹에서 Administrator 사용자 계정을 제거하려고 할 때 출력되는 오류 메세지 출력 화면으로 '로컬 사용자 및 그룹' 스냅인에서 같은 작업을 시도하려고 할 때 받게되는 오류 메세지와 동일한 메세지임을 알 수 있다. 그러나, 이 역시도 '개요' 부분에서 설명했던 제약 조건의 영향을 받는다는 사실을 잊지 말기 바란다.

보안

당연한 얘기겠지만 ECUM 프로그램을 처음 접한 분들이 가질 수 밖에 없는 의문은 바로 '이 프로그램을 사용하는 경우 보안쪽에 문제는 없을까?'가 아닐까 한다. 사실 이 정도 의문은 가지게 되는 것이 당연한 일일 것이다. 기본적으로 이미 몇 차례 얘기를 했지만 ADSI 기술을 사용해서 계정 관리 작업을 시도할 경우 적절한 권한이 없다면 접근 자체가 허락되지 않는다. 그래서, 필자가 ECUM을 제작할 때 어려움을 겪었던 점은 보안을 철저히 하는 부분이 아니라, 오히려 근본적으로 접근이 허락되지 않는 웹 환경에서의 계정에 대한 접근을 허용하도록 만드는 일이었다.

필자는 몇 가지 가능한 방법 중에서 가장 간단한 방법을 선택했는데 그것은 모든 중요한 페이지의 처음 부분에 다음과 같은 코드 스니펫을 인클루드시키는 방법이다. 실제로 이 코드는 ecum_login.asp 파일에 작성되어져 있으며 해당 파일은 대부분의 중요한 파일들의 코드 도입부에 인클루드 되어 있다.

  '****************************************************************
  '* 아래 몇 줄의 코드는 윈도우즈 사용자 인증을 강제적으로 진행하는
  '* 코드다.
  '****************************************************************

  If Request.ServerVariables("LOGON_USER") = "" Then
    Response.Status = "401 Authorization Required"
    Response.End
  End If

필자의 다른 글을 읽어보신 분들에게 이 코드는 어느 정도 익숙한 코드일 것이다. 이 코드는 인터넷 서비스 관리자에서 '익명 액세스'를 설정했는지 여부와는 상관 없이 강제적으로 윈도우즈 인증을 요구한다. 따라서, 올바른 윈도우즈 계정을 모르고 있는 사용자는 ECUM 프로그램 대한 접근 자체가 아예 불가능하며, 설사 로그인에 성공했다고 하더라도 해당 계정에 적절한 권한이 없다면 오류가 발생하면서 역시 접근이 불가능해 진다. 위의 코드는 다른 경우에도 때때로 유용하게 쓰이곤 하는데 그 대표적인 응용 사례를 필자의 다른 글에서도 찾아볼 수가 있다. 이에 대해서 관심을 가지고 계신 분들은 다음의 글에서 박스안에 따로 정리된 부분을 참고해보기 바란다.

이 코드의 요점은 ASP 프로그램에 인터넷 익스플로러에게 강제로 인증을 요구하는 헤더를 보낸다는 점에 있다. 그리고, 헤더를 받은 인터넷 익스플로러는 일단 내부적으로 현재 윈도우즈에 로그인한 사용자의 계정을 가지고 로그인을 시도하고 이 과정에 실패할 경우에만 사용자에게 계정과 패스워드를 입력하는 다이얼로그 박스를 출력한다. 이 기술의 원리와 관련된 보다 상세한 내용은 다음의 문서를 참고하기 바란다. 한 가지 주의해야 할 점은 이 문서에 기술되어 있는 것처럼 해당 기술은 비단 IIS와 인터넷 익스플로러 간에서만 통용되는 것이 아니라 윈도우즈 플랫폼을 기반으로 하는 HTTP 서버 또는 프록시와 HTTP 응용 프로그램간의 인증 전반에 걸친 내용이라는 점이다.

이런 과정을 거쳐서 ECUM 프로그램에 로그인을 하고 난 다음에는 필자가 조금이라도 더 보안을 강화하기 위해서 마련한 한 가지 과정을 더 거쳐야만 한다. 일반적인 윈도우즈의 기본 설정은 반드시 Administrators 그룹의 멤버가 아니라도 몇몇 특정한 그룹의 멤버들에게는 계정 관리를 허용해주고 있다. 또는 계정 관리자가 메뉴얼 작업을 통해서 특정한 그룹의 멤버들에게 직접 계정 관리 권한을 주었을 수도 있다. 반면, 필자는 ECUM 프로그램이 기본적으로 웹에서 운영되는 프로그램이라는 점을 감안해서 현재 로그인한 사용자가 Administrators 그룹의 멤버가 아닌 경우에는 무조건 ECUM 프로그램을 사용할 수 없도록 제한했다. 이 조건을 만족시키지 못하면 다음과 같은 ECUM 사용자 권한 오류 메세지를 만나게 될 것이다.

ECUM 사용자 권한 오류 메세지 화면

이 작업을 수행하는 실제 코드는 ecum_auth.asp 파일에 작성되어 있으며 해당 파일은 사용자 계정의 목록을 출력하는 userlist.asp 파일과 그룹의 목록을 출력하는 grouplist.asp 파일, 그리고 핵심적인 처리를 대부분 전담하는 프로시저 파일인 func_set.asp 파일에 인클루드 되어 있다. 따라서 현재 로그인한 사용자의 계정이 앞에서 얘기한 모든 조건을 충족시키지 못한다면, 가장 기본적인 정보라고도 말할 수 있는 목록조차도 접근할 수가 없을 뿐만 아니라 그 어떠한 작업도 실행할 수가 없다. 만약, 어떤 이유로 인해서 상기 제약조건을 완화하고자 한다면 직접 해당 부분의 코드를 수정해주어야만 한다.

ADSI를 이용한 사용자 계정 및 그룹 관리: 개체 목록

매번 ADSI 기술과 관련하여 얘기를 할 때마다 반복해서 강조하는 소리지만, ADSI 기술을 사용하는 작업의 약 절반 정도는 ADsPath를 작성하거나 얻어내는 과정이다. 그리고, 그 밖에 생각해 볼 수 있는 또 다른 중요한 작업으로는 적절한 ADSI 제공자를 선택하는 과정을 꼽을 수 있다. ECUM 프로그램의 경우에는 WinNT 제공자를 바탕으로 작성되었으므로 먼저 이에 대해서 알아보는 것이 당연한 순서일 것이다.

윈도우즈 서버 2003, 윈도우즈 XP, 그리고 윈도우즈 2000 제품군에는 ADSI WinNT 제공자가 기본적으로 설치되어 있다. 그러나, 클라이언트 운영체제가 윈도우즈 95/98/ME이거나 윈도우즈 NT 4.0 워크스테이션 또는 윈도우즈 NT 4.0 멤버 서버인 경우에는 반드시 ADSI 2.5 서비스 인터페이스나 액티브 디렉터리 클라이언트 확장(Active Directory Client Extensions)을 추가로 설치해줘야만 한다. 그러나, 이는 ADSI WinNT 제공자의 모든 기능을 완벽하게 사용하고자 할 경우의 이야기고, 지금과 같이 웹 서버상에서 로컬 사용자 계정이나 그룹만을 대상으로 작업이 이루어지는 경우에는 해당되지 않는 이야기다.

다음 링크는 윈도우즈 NT 서버 4.0을 위한 ADSI 2.5 서비스 인터페이스의 다운로드 페이지다. 만약, 윈도우즈 NT 서버 4.0 상에서 ECUM 프로그램을 운영할 경우에는 여기에서 ADSI 2.5 서비스 인터페이스를 다운로드 받아서 설치하면 되는데, 한글 버전 설치 프로그램이 없으므로 영문 버전을 다운로드 받아서 설치한다.

Active Directory Services Interfaces 2.5 : [Microsoft Windows NT Server 4.0; ADSI 2.5.]

그리고, ECUM 프로그램을 운영하는 문제와 하등의 관계는 없지만 윈도우즈 95 또는 98 기반의 운영체제를 위한 액티브 디렉터리 클라이언트 확장의 설치 프로그램은 윈도우즈 2000의 설치 CD에 포함되어 있으며 윈도우즈 NT 4.0 워크스테이션을 위한 액티브 디렉터리 클라이언트 확장의 설치 프로그램은 다음의 링크에서 다운로드가 가능하므로 필요한 경우 해당 운영체제에 알맞은 버전을 설치하여 사용하면 된다. 그리고, 이 페이지에서는 한글 버전 설치 프로그램도 같이 제공되므로 참고하기 바란다.

Active Directory Client Extensions for Windows NT Workstation 4.0

다음의 목록은 WinNT 제공자에서 사용할 수 있는 ADsPath의 패턴이다. 모든 WinNT 제공자 상의 ADsPath는 이 패턴 목록들 중에서 한 가지 패턴을 따르게 되는데, 여기서 <도메인>은 해당 도메인의 NetBIOS 이름이나 DNS 이름을 뜻하고 <서버>는 해당 도메인 내에 위치한 특정 서버의 NetBIOS 이름을 뜻한다. <경로>의 경우는 'PRINT_SERVER/PRINTER'와 같은 개체의 경로를 뜻하며, <개체 이름>은 얻고자 하는 실제 개체의 이름이다. 마지막으로 <개체 클래스>는 얻고자 하는 실제 개체의 ADSI 스키마 클래스 이름이다.

  • WinNT:
  • WinNT://<도메인>
  • WinNT://<도메인>/<서버>
  • WinNT://<도메인>/<경로>
  • WinNT://<도메인>/<개체 이름>
  • WinNT://<도메인>/<개체 이름>, <개체 클래스>
  • WinNT://<서버>
  • WinNT://<서버>/<개체 이름>
  • WinNT://<서버>/<개체 이름>, <개체 클래스>

이해를 돕기 위해서 한 가지 예를 들어보자. 'WinNT://SALES/EgoCube,User'라는 AdsPath의 경우, 'SALES'가 도메인 이름이라고 가정한다면 이 ADsPath가 의미하는 SALSE 도메인의 EgoCube라는 도메인 사용자 계정이 된다. 여기에서 중요한 점은 바로 <개체 클래스>에 해당하는 'User' 부분인데, 이 <개체 클래스>는 해당 개체의 검색과 바인딩 작업 수행 속도 향상에 상당한 영향을 미친다. 가령, <개체 클래스> 정보가 없는 경우, 'EgoCube'라는 문자열만 가지고서는 이것이 사용자 계정인지 아니면 그룹의 이름인지 그것도 아니면 서비스의 이름인지 등을 판단하기가 매우 어렵다. 따라서, 작업 수행 속도가 당연히 느려질 수 밖에 없는 반면, 이 예에서처럼 'User'와 같은 <개체 클래스>가 지정되는 경우에는 단순히 해당 클래스 집합만을 대상으로 검색하고 바인딩하면 되므로 상대적으로 처리 속도가 빠를 수 밖에 없다.

이번에는 ECUM 프로그램의 실제 코드를 살펴보도록 하자. 다음의 코드는 사용자 계정의 목록을 테이블 형태로 출력해주고 정렬과 필터링 등의 기능들을 제공해주는 userlist.asp 파일의 307 라인부터 337 라인까지의 코드이다. 라인수가 조금 많아 보일지도 모르겠지만 실제 처리 내용은 매우 이해하기 쉬운 간단한 작업뿐이므로 한 라인씩 차분히 살펴보도록 하겠다.

  '****************************************************************
  '* ADSI 객체를 얻고 User 클래스에 대해서 필터링한다. ADSI 리소스 
  '* 는 상당히 귀중한 자원이므로 사용이 끝나면 바로 해제해 준다.
  '****************************************************************
  
  Set oADsContainer = GetObject("WinNT://" & ECUM_COMPUTER & ",Computer")
  
  '** 사용자만 필터링한다.
  oADsContainer.Filter = Array("User")
  
  '** 커스텀 레코드셋에 정보를 담는다.
  Set oRec = Server.CreateObject("ADODB.RecordSet")
  oRec.CursorLocation = 3
  With oRec
    .Fields.Append "rsName",  200, 256
    .Fields.Append "rsFName", 200, 512
    .Fields.Append "rsDesc",  200, 1024
    .Fields.Append "rsAccountDisabled", 11
    .Open

    For Each oADsUser In oADsContainer
      .AddNew
      .Fields("rsName")  = oADsUser.Name
      .Fields("rsFName") = oADsUser.FullName
      .Fields("rsDesc")  = oADsUser.Description
      .Fields("rsAccountDisabled") = oADsUser.AccountDisabled
      .Update
    Next
  End With

  Set oADsContainer = Nothing

먼저 로컬 컴퓨터에 존재하고 있는 모든 사용자 계정을 User 개체의 컬렉션 형태로 얻어내는 초반부의 코드를 주의 깊게 살펴보기 바란다. 이 글의 앞에서부터 계속 강조해온 것처럼 ADSI 프로그래밍 작업시 가장 먼저 해야할 일은 ADsPath를 구성하는 것이다. 그런데, ECUM 프로그램의 모든 기능들은 로컬 컴퓨터의 사용자 계정이나 그룹만을 대상으로 제한되어져 있기 때문에, 이 경우 도메인의 이름은 사용되지 않으며 단지 로컬 컴퓨터의 NetBIOS 이름만 사용될 뿐이다. 로컬 컴퓨터의 NetBIOS 이름은 윈도우즈 스크립팅 호스트에서 제공해주는 네트워크 개체(WScript.Network)를 사용하여 동적으로 얻어오는데 위의 코드에서 붉은색으로 강조된 ECUM_COMPUTER 변수에 바로 이 값이 들어있다. 동적으로 로컬 컴퓨터의 NetBIOS 이름을 얻어내는 코드는 ASP 라이브러리 폴더 하위의 ecum_enum.asp 파일에 구현되어져 있으므로 참고하기 바란다.

따라서, 현재 로컬 컴퓨터의 이름이 EgoCube라고 가정한다면 위의 코드에서 첫 번째 라인은 다음의 코드와 완벽하게 동일한 의미를 갖게 된다. 즉, 다시 말해서 GetObject() 함수에 ADsPath 인자값으로 로컬 컴퓨터의 NetBIOS 이름이 결합된 "WinNT://EgoCube,Computer" 라는 문자열을 전달하고 그 결과값으로 WinNT 제공자로부터 제공되어지는 ADSI 개체 중 하나인 Computer 개체의 형태로 로컬 컴퓨터 개체를 리턴받은 후, 미리 선언해두었던 oADsContainer 변수에 할당하는 것이다.

  Set oADsContainer = GetObject("WinNT://EgoCube,Computer")

이렇게 얻어낸 Computer 개체는 내부적으로 IADsContainer 인터페이스를 구현하고 있는데, 이 인터페이스는 해당 개체의 하위에 존재하는 자식 ADSI 개체들에 대한 컬렉션 스타일의 접근 기능이나 새로운 자식 ADSI 개체를 생성하거나 삭제하는 등의 관리 기능들을 제공해준다. 따라서, IADsContainer 인터페이스에서 제공해주는 기능들을 이용하면 Computer 개체의 자식 개체들 중 하나인 User 개체들에 대한 접근이 가능해진다. 그런데, 문제는 현재의 코드가 로컬 컴퓨터에 존재하는 모든 User 개체들 뿐만 아니라 Group 개체나 Service 개체들까지도 모두 리턴해준다는 점이다. 반면, 우리가 원하는 것은 User 개체들뿐이므로 불필요한 항목들을 걸러주기 위한 추가적인 작업이 한 단계 더 요구되고, 결국 위에서 붉은색으로 강조된 다음의 코드가 이 과정을 처리해주게 된다.

  oADsContainer.Filter = Array("User")

여기에 사용되는 Filter 프로퍼티는 IADsContainer 인터페이스로부터 제공되는 프로퍼티로서 원하는 자식 개체들만을 필터링 할 수 있는 기능을 제공해주는데, 프로퍼티에 각각의 항목에 필터링하기 원하는 개체 종류의 스키마 클래스 이름을 문자열로 입력한 Variant 배열을 설정해주면 된다. 이를테면 지금과 같은 경우에는 User 개체들의 집합만을 얻고자 하는 것이므로 위의 코드처럼 User 개체의 클래스 이름인 "User" 문자열을 배열에 입력하고 그 배열을 Filter 프로퍼티에 설정하면 기대하고 있는 결과를 얻을 수가 있는 것이다.

이제 루프를 돌면서 필요한 프로퍼티값들을 얻고 커스텀 레코드셋에 저장하기만 하면 된다. 루프를 돌면서 값을 바로 출력하지 않고 일단 커스텀 레코드셋에 정보를 저장하는 이유는 바로 정렬과 필터링 기능을 보다 손쉽게 구현하기 위해서이다. 커스텀 레코드셋을 이용하여 정렬과 필터링 기능을 구현하는 방법에 대해서는 필자가 이미 다른 글에서 한 차례 설명한 적이 있으므로 이 글에서는 다시 다루지 않도록 하겠다. 관심이 있는 분들은 다음의 글을 참고하기 바란다.

그리고, 루프를 돌 때 다음 코드와 같이 컬렉션 스타일의 For Each ... Next 문을 사용할 수 있다는 점도 주의 깊게 살펴봐두기 바란다. 이런 특성은 이미 앞에서도 한 번 설명했듯이 IADsContainer 인터페이스로부터 제공되는 기능이다. 다음으로 이제 우리에게 필요한 것은 User 개체의 어떤 정보들을 어떤 프로퍼티를 통해서 얻어낼 수 있는지를 알아내는 것이고, 따라서 User 개체가 지원하는 인터페이스들의 종류와 그 각각의 인터페이스의 구조에 대해서 알아볼 필요가 있다.

  For Each oADsUser In oADsContainer
    .AddNew
    .Fields("rsName")  = oADsUser.Name
    .Fields("rsFName") = oADsUser.FullName
    .Fields("rsDesc")  = oADsUser.Description
    .Fields("rsAccountDisabled") = oADsUser.AccountDisabled
    .Update
  Next

이런 정보들을 알아보기 위해서는 역시 MSDN의 해당 문서를 찾아보는 것이 가장 쉽고 빠른 길이다. 다음 문서의 다이어그램상에서 User 개체에 대한 부분을 찾아보기 바란다.

다이어그램에 따르면 User 개체는 IADs 인터페이스와 IADsUser 인터페이스 그리고 IADsUserPropertyList 인터페이스, 이렇게 모두 세 가지 종류의 인터페이스를 구현하고 있다. 그 중에서도 IADsUser 인터페이스는 네 가지 가장 기본적인 ADSI 개체 인터페이스 중의 하나이므로 주의 깊게 살펴볼 필요가 있다. 그리고, IADsUser 인터페이스야말로 바로 우리가 원하는 User 개체에 대한 실제 구현 인터페이스이므로 가장 집중적인 학습이 필요하다. 반면, IADsUserPropertyList 인터페이스는 프로퍼티 캐쉬와 관련된 조금 특수한 인터페이스인데 이 인터페이스는 글의 문맥상 전혀 필요로 하지 않을 뿐더러 활용도도 매우 낮으므로 일단 이 인터페이스는 이자리에서는 다루지 않고 넘어가기로 한다.

이 다이어그램에서 User 개체 우측에 표시된 인터페이스명을 클릭하면 각각의 인터페이스들의 메서드 및 프로퍼티들의 목록과 기타 상세한 설명들이 기록된 문서들로 이동할 수 있으므로 각자 꼭 한 번씩은 이 문서들을 직접 읽어보기 바란다. 이 문서들을 읽어보면 어떤 정보를 어떤 프로퍼티를 통해서 얻을 수 있는지, 자신이 원하는 기능이 실제로 제공되고는 있는지 등의 필요한 정보들을 손쉽게 얻을 수 있다. 예를 들어서, 위의 코드에서 사용자 계정의 이름 정보는 IADs 인터페이스의 Name 프로퍼티를 통해서 가져온 것이고 전체 이름과 설명 정보는 각각 IADsUser 인터페이스의 FullName 프로퍼티와 Description 프로퍼티를 통해서 가져온 것이다. 이처럼 인터페이스 구조는 COM 기반의 개체를 사용하기 위한 지침서와 같은 것이므로 주의 깊게 살펴볼만한 충분한 가치를 지니고 있다.

그룹에 대해서도 정확하게 동일한 요령으로 접근이 가능한데, 너무나도 당연한 얘기겠지만 이 경우에는 User 개체가 아니라 Group 개체를 대상으로 필터링해야 하고 커스텀 레코드셋의 필드들도 적절히 수정해줘야만 한다. 이 부분에 대한 실제 코드 구현은 grouplist.asp 파일의 263 라인부터 289 라인까지므로 각자 한 번씩 살펴보기 바란다. 이번에도 역시 주의해서 살펴보아야 될 부분은 Group 개체가 구현하고 있는 인터페이스다. 위의 다이어그램에서 다시 한 번 Group 개체에 대한 부분을 찾아보기 바란다. Group 개체는 IADsUser 인터페이스 대신 IADsGroup 인터페이스를 지원해 준다는 사실을 알 수가 있는데 이는 인터페이스 이름만 보면 누구라도 쉽게 수긍이 가는 일이다.

다음 목록은 지금까지 얘기한 여러가지 핵심 인터페이스들에 대해서 설명되어 있는 MSDN 문서들의 링크들이다. 필자가 설명한 모든 내용들은 결국 이 문서들 안에 담겨져 있는 내용들을 바탕으로 한 것이라고도 말할 수 있으므로 관심을 가지고 계신 분들은 해당 문서들을 한 번 꼼꼼히 읽어보기를 권한다.

이 네 가지 인터페이스만 확실하게 이해하면 ECUM 프로그램에 사용된 ADSI 기술과 관련된 대부분의 내용들을 쉽게 파악할 수 있다. 먼저, IADs 인터페이스는 모든 ADSI 개체들이 반드시 구현해야만 되는 가장 기본적인 인터페이스로, 이를테면 비주얼 베이직의 Variant 형이나 C#의 Object 형과 비슷한 위치를 점하고 있다고 보면 이해가 쉬울 것이다. 두 번째 IADsContainer 인터페이스는 주로 현재 개체가 두 개 이상의 자식 개체들을 가지고 있을 때 제공되는 인터페이스로, 역시 비주얼 베이직의 컬렉션과 유사한 기능들을 제공해준다. 그리고, 마지막으로 세 번째와 네 번째 인터페이스는 각각 User 개체와 Group 개체의 특성들을 실제로 구현한 인터페이스라고 생각하면 된다.

ADSI를 이용한 사용자 계정 및 그룹 관리: 개체 속성값 관리

이번에는 각각의 사용자 계정이나 그룹의 여러가지 속성값들을 읽어오거나 설정하는 방법에 대해서 알아보자. 먼저 머리속으로 이런 작업들을 처리하기 위한 일련의 과정들을 떠올려보기 바란다. 가장 먼저 처리해야 할 작업은 관리하고자 하는 대상, 즉 User 개체나 Group 개체를 얻는 일이다. 따라서, 역시 이 경우에도 ADsPath를 올바르게 작성하는 일이 선행되어야만 한다. 그리고, 그 뒤에는 얻어낸 개체에서 적당한 프로퍼티값을 읽어오거나 새 값을 설정해서 해당 사용자 계정이나 그룹의 속성값들을 관리해 주면 되는데, 이 과정에서 앞에서 설명한 몇 가지 인터페이스들에 대한 이해가 요구되고 그 외에는 몇 가지 보편적인 작업들만 처리해주면 된다.

그러면, 얘기한 순서에 따라서 먼저 ADsPath를 구성해보도록 하자. 특정 사용자 계정의 기본 정보를 출력해주는 파일인 dlg_userinfo.asp 파일의 67 라인을 보면 다음과 같은 코드를 찾을 수 있다. 일단 사용자 계정에 대한 작업을 사례로 들어서 설명하기는 하지만 그룹의 경우에도 역시 동일한 과정을 밟는다는 점을 염두에 두기 바란다.

  Set oADsUser = GetObject("WinNT://" & ECUM_COMPUTER & "/" & reqName & "," & reqClass)

이 코드에 사용된 ECUM_COMPUTER 변수에는 역시 앞의 경우와 동일하게 로컬 컴퓨터의 NetBIOS 이름이 담겨 있고, reqName 변수에는 사용자가 목록 페이지에서 마우스로 클릭하여 선택한 사용자 계정의 이름이 담겨져 있다. 그리고, reqClass 변수에는 "User"라는 문자열이 담겨져 있는데 이 문자열은 자바스크립트 코드내에 하드코딩 되어 있으며 그룹에 대한 해당 코드에는 이 문자열 대신 "Group"이라는 문자열이 설정되어 있음은 물론이다. 따라서, 만약 로컬 컴퓨터의 이름이 EgoCube고 사용자가 Administrator 계정을 선택한 경우라면 상기 코드는 다음 코드와 완벽하게 동일한 의미를 갖게 된다.

  Set oADsUser = GetObject("WinNT://EgoCube/Administrator,User")

이제 이렇게 얻어낸 작업 대상 개체로부터 적절한 프로퍼티값을 읽어오거나 새로운 값을 설정해주면 되는데, 이미 User 개체 또는 Group 개체가 구현하고 있는 인터페이스들의 종류나 그 각각의 인터페이스로부터 필요한 정보들을 찾을 수 있는 방법을 알고 있으므로 별다른 어려움 없이 원하는 정보를 얻을 수 있을 것이다. 다음은 그 중 몇 가지 사례로 편의상 User 개체의 프로퍼티를 예로 소개한다.

  '** IADs 인터페이스 :: 현재 개체의 이름, 이 경우엔 사용자 계정의 이름
  Response.Write oADsUser.Name & "<br>"
    
  '** IADs 인터페이스 :: 현재 개체의 ADsPath 문자열
  Response.Write oADsUser.ADsPath & "<br>"
    
  '** IADsUser 인터페이스 :: 전체 이름
  Response.Write oADsUser.FullName & "<br>"
   
  '** IADsUser 인터페이스 :: 설명
  Response.Write oADsUser.Description & "<br>"
   
  '** IADsUser 인터페이스 :: 사용자 계정을 사용할 수 있는지 여부, Boolean 형태로 리턴
  Response.Write oADsUser.AccountDisabled & "<br>"
    
  '** IADsUser 인터페이스 :: 사용자 계정의 잠김 여부, Boolean 형태로 리턴
  Response.Write oADsUser.IsAccountLocked & "<br>"

그리고, 대단히 접하기 힘든 예외적인 경우로 WinNT 제공자에서는 오직 User 개체에 대해서만 예닐곱 개의 특수한 프로퍼티를 제공해주고 있는데, 이 프로퍼티들에 대한 언급은 IADsUser 인터페이스에 대한 문서에서는 다루어지지 않으므로 반드시 이 사실을 기억해두기 바란다. 이 프로퍼티들에 대한 전체 목록과 기타 상세한 정보들은 다음의 문서에서 찾아볼 수 있다.

참고로 이 문서를 살펴보면 목록중에 ObjectSID라는 프로퍼티를 찾아볼 수가 있는데, 설명을 읽어보면 이 프로퍼티가 바로 위에서 설명했던 보안 식별자를 리턴해 준다고 되어있을 것이다. 필자가 앞에서 ADSI만으로는 보안 식별자를 얻을 수 있는 방법이 전무하다고 설명했던 것을 기억하고 있는 분들은 아마 상당히 혼란스러울 것으로 짐작된다. 문제는 이 ObjectSID라는 프로퍼티가 단지 User 개체에만 제공된다는 점과 리턴되는 데이터 형식이 Octet String이라는 점이다. 이 Octet String이라는 데이터 형식은 쉽게 말해서 바이트 배열을 말하고 있는 것인데 안타깝게도 VBScript 계열의 프로그래밍 언어에서는 이 형식을 제대로 처리할 수가 없다. 결론적으로 이 경우에도 역시 컴포넌트 등의 외부 모듈 지원이 요구되는 것이다. 다음은 WinNT 제공자 커스텀 프로퍼티의 간단한 사용 사례들이다.

  '** 커스텀 프로퍼티 :: 프로퍼티값이 1 이면 패스워드 만료
  Response.Write oADsUser.PasswordExpired & "<br>"
    
  '** 커스텀 프로퍼티 :: ADS_USER_FLAG_ENUM 열거형 플래그 값으로 비트 연산 필요
  Response.Write oADsUser.UserFlags & "<br>"

특히 이중에서도 UserFlags 프로퍼티는 조금 더 특별한데 ADS_USER_FLAG_ENUM 열거형 값으로 구성된 플래그로 여러 가지 데이터를 복합적으로 갖고 있다. 그래서, 각각의 값들을 제대로 읽어내거나 설정해주려면 비트 연산 작업이 필요하다. 참고로 이 프로퍼티 하나를 가지고 얻어내거나 설정할 수 있는 속성값의 종류는 무려 스물 한 가지에 달한다. 다음의 링크는 ADS_USER_FLAG_ENUM 열거형의 전체 목록과 간단한 설명이 담겨져 있는 MSDN 문서이다.

비트 연산에 익숙하지 않은 분들을 위해서 한 가지 간단한 예를 들어보도록 하겠다. 가령, 다음의 코드는 '암호 변경할 수 없음' 속성을 의미하는 ADS_UF_PASSWD_CANT_CHANGE 열거자가 플래그에 설정된 상태인지 여부를 조사하는 코드다. 만약 해당 열거자가 플래그에 설정되어 있는 상태라면 AND 연산시킨 결과값이 열거자의 값과 동일할 것이다. 이 코드에 사용된 ADS_UF_PASSWD_CANT_CHANGE 열거자 상수를 비롯한 전체 열거자 상수는 ASP 라이브러리 폴더 하위의 ecum_enum.asp 파일에 정의되어 있으므로 참고하기 바란다. 이 상수들의 값은 위 문서에 정의된 ADS_USER_FLAG_ENUM 열거형의 값과 그대로 동일하게 설정한 것이다.

  If (oADsUser.UserFlags AND ADS_UF_PASSWD_CANT_CHANGE) = ADS_UF_PASSWD_CANT_CHANGE Then
    '** 암호 변경할 수 없음 : True
  Else
    '** 암호 변경할 수 없음 : False
  End If

지금까지의 코드들은 각각의 사용자 계정이나 그룹의 여러 가지 속성값들을 읽어오는 방법에 대한 설명이었다. 역으로 속성값들을 설정하는 방법도 지금까지의 과정과 별로 다른 점이 없다. 동일한 방법으로 User 개체 또는 Group 개체를 구한 다음 적당한 프로퍼티에 원하는 값을 설정하기만 하면 된다. 다만, 프로퍼티값을 수정한 뒤에는 항상 SetInfo() 메서드를 호출해야만 실제로 수정한 값들이 저장된다는 점만 명심하도록 한다. SetInfo() 메서드에 대해서는 이미 ADSI에 관한 과거의 글들에서 여러 번에 걸쳐 설명했으므로 이 자리에서는 다시 설명하지 않도록 하겠다. 궁금하신 분들은 각자 IADs 인터페이스에 관한 문서에서 해당 메서드에 관한 부분을 읽어보기 바란다.

ADSI를 이용한 사용자 계정 및 그룹 관리: 개체 생성 또는 제거

이제 마지막으로 새로운 사용자 계정 또는 그룹을 생성하거나 제거하는 방법에 대해서 알아보자. 코딩의 분량이라는 관점에서 보면 아마도 이 작업이 가장 쉬운 작업 중 하나일 것이다. 사실 논리적인 측면에서 볼 때에도 이는 마찮가지인데 요점은 비주얼 베이직의 컬렉션 개체에 새로운 컬렉션 항목을 추가하거나 기존의 컬렉션 항목을 제거하는 방법과 거의 동일한 방법을 사용한다는 점에 있다. 이런 스타일의 접근 방식은 예상을 할 수 있는 바와 같이 IADsContainer 인터페이스로부터 제공되는 것으로 앞서 한 차례 설명한 바가 있다.

이 인터페이스에 관한 문서를 살펴보면 Create() 메서드와 Delete() 메서드를 찾아볼 수가 있는데 각각 현재 개체의 자식 개체를 새로 생성하거나 제거하는 작업을 처리해준다. 일단 먼저 Create() 메서드에 대해서 살펴보도록 하겠다. 다음은 func_set.asp 파일의 84 라인부터 86 라인까지의 코드로 새로운 사용자 계정을 생성하는 작업을 처리하는 코드의 도입부분이다.

  Set oADsContainer = GetObject("WinNT://" & ECUM_COMPUTER & ",Computer")
  Set oADsUser = oADsContainer.Create("User", Trim(aryTemp01(0)))
  oADsUser.SetInfo

직접 확인할 수 있는 것처럼 무척 간단한 코드로서 새로운 사용자 계정을 만들 Computer 개체를 얻은 다음에, IADsContainer 인터페이스의 Create() 메서드를 호출해서 실제로 새로운 User 개체를 생성한 후, 마지막으로 언제나처럼 SetInfo() 메서드를 호출해서 마무리한다. 아마도 이 코드 중에서 여러분들이 처음 접하는 부분이라고는 Create() 메서드의 사용법 정도가 전부일 것이다. Create() 메서드의 첫 번째 인자는 새로 작성하고자 하는 개체의 스키마 클래스 이름이다. 그도 당연한 것이 Computer 개체같은 경우에는 User 개체뿐만 아니라 Group 개체나 Service 개체 역시도 자식 개체로 가질 수가 있기 때문에 현재 작성하고자 하는 개체가 어떤 스키마 클래스의 개체인지를 알려줘야만 하는 것이다. 그리고, 두 번째 인자는 새로 작성되는 개체의 이름으로 이것이 Create() 메서드 사용법의 전부이다.

기존 개체를 제거하는 Delete() 메서드의 사용법 역시 별반 다르지 않다. 다음은 동일한 파일의 171 라인부터 173 라인까지의 코드로 선택한 특정 사용자 계정을 제거하는 작업을 처리하는 코드이다. 따로 설명을 해야만 할 만큼 새로운 부분이 존재하지 않는다.

  Set oADsContainer = GetObject("WinNT://" & ECUM_COMPUTER & ",Computer")
  oADsContainer.Delete CStr(aryTemp01(1)), CStr(aryTemp01(0))
  Set oADsContainer = Nothing

단만 한 가지 주의해야 할 점은, 이 메서드는 리프 개체나 자식 개체가 존재하지 않는 개체만을 삭제할 수 있다는 점이다. 만약, 제거하고자 하는 개체에 한 단계 이상의 자식 개체들이 존재하고 있다면, 해당 개체를 삭제하기 위해서는 Delete() 메서드 대신에 IADsDeleteOps 인터페이스의 DeleteObject() 메서드를 사용해야만 한다. 그리고, Delete() 메서드를 호출하는 경우에는 결과를 적용하기 위해서 SetInfo() 메서드를 호출할 필요가 없으며 호출되는 그 즉시 작업이 처리된다.