외부 역할 매핑

프라이빗 클라우드용 Edge v4.18.01

외부 역할 매핑을 사용하면 자체 그룹 또는 역할을 Apigee Edge에서 생성된 역할 기반 액세스 제어(RBAC) 역할 및 그룹에 매핑할 수 있습니다. 이 기능은 Edge Private Cloud에서만 사용할 수 있습니다.

새로운 기능

4.18.01 이전의 프라이빗 클라우드용 Edge 출시 버전에 대한 외부 역할 매핑 서비스가 지원 중단되었습니다. 외부 역할 매핑 버전 4.18.01은 버그가 수정되고 새로운 기능이 추가된 업데이트 버전입니다.

  • 액세스 권한이 있는 사용자로 인증할 때 인증 403 금지 응답이 수신되는 문제가 해결되었습니다.
  • 이제 외부 역할 매핑에서 X-Apigee-Current-User 헤더가 지원됩니다. 적절한 액세스 권한 (sysadmin)이 있는 사용자는 이제 다른 사용자에게 할당된 역할을 볼 수 있습니다.

기본 요건

  • 이 구성을 수행하려면 전역 시스템 관리자 사용자 인증 정보가 있는 Apigee 프라이빗 클라우드 시스템 관리자여야 합니다.
  • Apigee Edge 프라이빗 클라우드 설치의 루트 디렉터리를 알아야 합니다. 기본 루트 디렉터리는 /opt입니다.

단계별 설정 예시

외부 역할 매핑을 설정하는 단계별 예시는 Apigee 커뮤니티 포럼의 이 도움말을 참조하세요.

기본 구성

외부 역할 매핑은 기본적으로 사용 중지되어 있습니다.

외부 역할 매핑 사용 설정

  1. 다음 구성을 완료하려면 먼저 ExternalRoleMapperServiceV2 인터페이스를 구현하는 Java 클래스를 만들고 Management Server 클래스 경로에 구현을 포함해야 합니다.

    /opt/apigee/edge-management-server/lib/thirdparty/

    이 구현에 대한 자세한 내용은 이 문서의 뒷부분에 있는 ExternalRoleMapperImpl 샘플 구현 정보 섹션을 참조하세요.
  2. Apigee Edge 관리 서버에 로그인한 후 관리 서버 프로세스를 중지합니다.
    > /opt/apigee/apigee-service/bin/apigee-service edge-management-server stop
  3. 텍스트 편집기에서 /opt/apigee/customer/application/management-server.properties를 엽니다. 이 파일이 존재하지 않으면 만듭니다.
  4. 속성 파일을 수정하여 다음 설정을 지정합니다.
    # 인증에 사용할 사용자 저장소입니다.
    # LDAP 사용자 저장소에 'externalized.authentication'을 사용합니다.
    # Note that for authorization, we continue to use LDAP.
    # 외부 인증 사용 설정에 대한 자세한 내용은 외부 인증 사용 설정을 참조하세요.
    conf_security_authentication.user.store=externalized.authentication

    #Enable the external authorizations role mapper.
    conf_security_externalized.authentication.role.mapper.enabled=true conf_security_externalized.authentication.role.mapper.implementation.class=
    com.customer.authorization.impl.ExternalRoleMapperImpl

    중요:
    위의 구성(ExternalRoleMapperImpl)에서 참조된 구현 클래스 및 패키지 이름은 예시일 뿐입니다. 이는 구현해야 하는 클래스이며 클래스 및 패키지 이름을 원하는 대로 지정할 수 있습니다. 이 클래스 구현에 관한 자세한 내용은 아래의 ExternalRoleMapperImpl 샘플 구현 클래스 정보를 참고하세요. 자체 그룹을 반영하기 위해 구현해야 하는 클래스입니다.
  5. management-server.properties 파일을 저장합니다.
  6. management-server.properties가 apigee 사용자의 소유인지 확인합니다.
    > chown apigee:apigee /opt/apigee/customer/application/management-server.properties
  7. 관리 서버를 시작합니다.
    > /opt/apigee/apigee-service/bin/apigee-service edge-management-server start

외부 승인 사용 중지

외부 승인을 사용 중지하려면 다음 안내를 따르세요.

  1. 텍스트 편집기에서 /opt/apigee/customer/application/management-server.properties를 엽니다. 파일이 없으면 만듭니다.
  2. 인증 사용자 저장소를 ldap으로 변경합니다.
    conf_security_authentication.user.store=ldap
  3. 이 속성을 false로 설정합니다.
    conf_security_externalized.authentication.role.mapper.enabled=false
  4. 관리 서버를 다시 시작합니다.
    > /opt/apigee/apigee-service/bin/apigee-service edge-management-server start

ExternalRoleMapperImpl 샘플 구현 정보

이전에 외부 역할 매핑 사용 설정에서 설명한 security.properties 구성 파일에서 다음 줄을 확인합니다.

externalized.authentication.role.mapper.implementation.class=com.customer.authorization.impl.ExternalRoleMapperImpl

이 클래스는 ExternalRoleMapperServiceV2 인터페이스를 구현하며 필수입니다. 각 그룹을 반영하는 이 클래스의 자체 구현을 만들어야 합니다. 완료되면 컴파일된 클래스를 JAR에 배치하고 해당 JAR을 다음의 Management Server 클래스 경로에 배치합니다.

/opt/apigee/edge-management-server/lib/thirdparty/

ExternalRoleMapperServiceV2를 구현하고 클래스 경로에서 액세스할 수 있으며 management-server.properties 구성 파일에서 올바르게 참조되는 한 클래스 및 패키지의 이름을 원하는 대로 지정할 수 있습니다.

아래에는 주석이 잘 달린 ExternalRoleMapperImpl 클래스의 샘플 구현이 나와 있습니다.

package com.customer.authorization.impl;

import com.apigee.authentication.*;
import com.apigee.authorization.namespace.OrganizationNamespace;
import com.apigee.authorization.namespace.SystemNamespace;
import java.util.Collection;
import java.util.HashSet;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;

/** *
* Sample Implementation constructed with dummy roles with expected namespaces.
*/

public class ExternalRoleMapperImpl
       implements ExternalRoleMapperServiceV2 {

   InitialDirContext dirContext = null;

   @Override
   public void start(ConfigBean arg0) throws ConnectionException {

       try {
           // Customer Specific Implementation will override the
           // ImplementDirContextCreationLogicForSysAdmin method implementation.
           // Create InitialDirContext based on the system admin user credentials.
           dirContext = ImplementDirContextCreationLogicForSysAdmin();
       } catch (NamingException e) {
           // TODO Auto-generated catch block
           throw new ConnectionException(e);
       }
   }

   @Override
   public void stop() throws Exception {
   }

   /**
    * This method should be replaced with customer's implementation
    * For given roleName under expectedNamespace, return all users that belongs to this role
    * @param roleName
    * @param expectedNamespace
    * @return All users that belongs to this role. For each user, please return the username/email that is stored in Apigee LDAP
    * @throws ExternalRoleMappingException
    */
   @Override
   public Collection<String> getUsersForRole(String roleName, NameSpace expectedNamespace) throws ExternalRoleMappingException {
       Collection<String> users = new HashSet<>();
       if (expectedNamespace instanceof SystemNamespace) {
           // If requesting all users with sysadmin role
           if (roleName.equalsIgnoreCase("sysadmin")) {
               // Add sysadmin's email to results
               users.add("sysadmin@wacapps.net");
           }
       } else {
           String orgName = ((OrganizationNamespace) expectedNamespace).getOrganization();
           // If requesting all users of engRole in Apigee LDAP
           if (roleName.equalsIgnoreCase("engRole")) {
               // Get all users in corresponding groups in customer's LDAP. In this case looking for 'engGroup';
               SearchControls controls = new SearchControls();
               controls.setSearchScope(1);
               try {
                   NamingEnumeration<SearchResult> res = dirContext.search("ou=groups,dc=corp,dc=wacapps,dc=net",
                           "cn=engGroup", new Object[]{"",""}, controls);
                   while (res.hasMoreElements()) {
                       SearchResult sr = res.nextElement();
                       // Add all users into return
                       users.addAll(sr.getAttributes().get("users").getAll());
                   }
               } catch (NamingException e) {
                   // Customer needs to handle the exception here
               }
           }
       }
       return users;
   }

   /**
    *
    * This method would be implemented by the customer and would be invoked
    * while including using X-Apigee-Current-User header in request.
    *
    * X-Apigee-Current-User allows the customer to login as another user
    *
    * Below is the basic example.
    *
    * If User has sysadmin role then it's expected to set SystemNameSpace
    * along with the expected NameSpace. Otherwise role's expectedNameSpace
    * to be set for the NameSpacedRole.
    *
    * Collection<NameSpacedRole> results = new HashSet<NameSpacedRole>();
    *
    * NameSpacedRole sysNameSpace = new NameSpacedRole("sysadmin",
    * SystemNamespace.get());
    *
    * String orgName =
    * ((OrganizationNamespace) expectedNameSpace).getOrganization();
    *
    * NameSpacedRole orgNameSpace = new NameSpacedRole ("orgadmin",
    * expectedNameSpace);
    *
    * results.add(sysNameSpace);
    *
    * results.add(orgNameSpace);
    *
    *
    * @param username UserA's username
    * @param password UserA's password
    * @param requestedUsername UserB's username. Allow UserA to request UserB's userroles with
    *                          UserA's credentials when requesting UserB as X-Apigee-Current-User
    * @param expectedNamespace
    * @return
    * @throws ExternalRoleMappingException
    */
   @Override
   public Collection<NameSpacedRole> getUserRoles(String username, String password, String requestedUsername, NameSpace expectedNamespace) throws ExternalRoleMappingException {
       /************************************************************/
       /******************** Authenticate UserA ********************/
       /************************************************************/

       // Customer Specific Implementation will override the
       // ImplementDnameLookupLogic method implementation.

       // obtain dnName for given username.
       String dnName = ImplementDnNameLookupLogic(username);
       // Obtain dnName for given requestedUsername.
       String requestedDnName = ImplementDnNameLookupLogic(requestedUsername);

       if (dnName == null || requestedDnName == null) {
           System.out.println("Error ");
       }

       DirContext dirContext = null;
       try {

           // Customer Specific Implementation will override the
           // ImplementDirectoryContextCreationLogic method implementation

           // Create a directory context with dnName or requestedDnName and password
           dirContext = ImplementDirectoryContextCreationLogic();

           /************************************************/
           /*** Map internal groups to apigee-edge roles ***/
           /************************************************/
           return apigeeEdgeRoleMapper(dirContext, requestedDnName, expectedNamespace);

       } catch (Exception ex) {
           ex.printStackTrace();
           System.out.println("Error in authenticating User: {}" + new Object[] { username });

       } finally {
           // Customer implementation to close
           // ActiveDirectory/LDAP context.
       }

       return null;

   }

   /**
    *
    * This method would be implemented by the customer and would be invoked
    * wihle using username and password for authentication and without the
    * X-Apigee-Current-User header
    *
    * The customer can reuse implementations in
    *      getUserRoles(String username, String password, String requestedUsername, NameSpace expectedNamespace)
    * by
    *      return getUserRoles(username, password, username, expectedNamespace)
    * in implementations.
    *
    * or the customer can provide new implementations as shown below.
    */

   @Override
   public Collection<NameSpacedRole> getUserRoles(String username, String password, NameSpace expectedNamespace) throws ExternalRoleMappingException {
       /*************************************************************/
       /****************** Authenticate Given User ******************/
       /*************************************************************/

       // Customer Specific Implementation will override the
       // ImplementDnameLookupLogic implementation.

       // Obtain dnName for given username or email address.
       String dnName = ImplementDnNameLookupLogic(username);

       if (dnName == null) {
           System.out.println("Error ");
       }

       DirContext dirContext = null;
       try {
           // Create a directory context with username or dnName and password
           dirContext = ImplementDirectoryContextCreationLogic();

           /************************************************/
           /*** Map internal groups to apigee-edge roles ***/
           /************************************************/
           return apigeeEdgeRoleMapper(dirContext, dnName, expectedNamespace);

       } catch (Exception ex) {
           ex.printStackTrace();
           System.out.println("Error in authenticating User: {}" + new Object[] { username });

       } finally {
           // Customer implementation to close
           // ActiveDirectory/LDAP context.
       }

       return null;
   }

   /**
    *
    * This method would be implemented by the customer and would be invoked
    * while using security token or access token as authentication credentials.
    *
    */
   @Override
   public Collection<NameSpacedRole> getUserRoles(String username, NameSpace expectedNamespace) throws ExternalRoleMappingException {

       /*************************************************************/
       /****************** Authenticate Given User ******************/
       /*************************************************************/

       // Customer Specific Implementation will override the
       // ImplementDnameLookupLogic implementation.

       // Obtain dnName for given username or email address.
       String dnName = ImplementDnNameLookupLogic(username);

       if (dnName == null) {
           System.out.println("Error ");
       }

       DirContext dirContext = null;
       try {
           // Create a directory context with username or dnName and password
           dirContext = ImplementDirectoryContextCreationLogic();

           /************************************************/
           /*** Map internal groups to apigee-edge roles ***/
           /************************************************/
           return apigeeEdgeRoleMapper(dirContext, dnName, expectedNamespace);

       } catch (Exception ex) {
           ex.printStackTrace();
           System.out.println("Error in authenticating User: {}" + new Object[] { username });

       } finally {
           // Customer implementation to close
           // ActiveDirectory/LDAP context.
       }

       return null;
   }

   /**
    *  This method should be replaced with Customer Specific Implementations
    *
    *  Provided as a sample Implementation of mapping user groups to apigee-edge roles
    */
   private Collection<NameSpacedRole> apigeeEdgeRoleMapper(DirContext dirContext, String dnName, NameSpace expectedNamespace) throws Exception {

       Collection<NameSpacedRole> results = new HashSet<NameSpacedRole>();

       /****************************************************/
       /************ Fetch internal groups *****************/
       /****************************************************/

       String groupDN = "OU=Groups,DC=corp,DC=wacapps,DC=net";
       String userFilter = "(user=userDnName)";
       SearchControls controls = new SearchControls();
       controls.setSearchScope(SearchControls.ONELEVEL_SCOPE);

       // Looking for all groups the user belongs to in customer's LDAP
       NamingEnumeration<SearchResult> groups = dirContext.search(groupDN,userFilter.replace("userDnName", dnName), new Object[] { "", "" }, controls);

       if (groups.hasMoreElements()) {
           while (groups.hasMoreElements()) {
               SearchResult searchResult = groups.nextElement();
               Attributes attributes = searchResult.getAttributes();
               String groupName = attributes.get("name").get().toString();

               /************************************************/
               /*** Map internal groups to apigee-edge roles ***/
               /************************************************/

               if (groupName.equals("BusDev")) {
                   results.add(new NameSpacedRole("businessAdmin",SystemNamespace.get()));

               } else if (groupName.equals("Engineering")) {
                   if (expectedNamespace instanceof OrganizationNamespace) {
                       String orgName = ((OrganizationNamespace) expectedNamespace).getOrganization();
                       results.add(new NameSpacedRole("orgadmin", new OrganizationNamespace(orgName)));
                   }

               } else if (groupName.equals("Marketing")) {
                   results.add(new NameSpacedRole("marketAdmin",SystemNamespace.get()));

               } else {
                   results.add(new NameSpacedRole("readOnly",SystemNamespace.get()));
               }
           }

       } else {
           // In case of no group found or exception found we throw empty roles.
           System.out.println(" !!!!! NO  GROUPS FOUND !!!!!");
       }
       return results;
   }

   /**
    * The customer need to replace with own implementations for getting dnName for given user
    */
   private String ImplementDnNameLookupLogic(String username) {
       // Connect to the customer's own LDAP to fetch user dnName
       return customerLDAP.getDnName(username);
   }

   /**
    * The customer need to replace with own implementations for creating DirContext
    */
   private DirContext ImplementDirectoryContextCreationLogic() {
       // Connect to the customer's own LDAP to create DirContext for given user
       return customerLDAP.createLdapContextUsingCredentials();
   }

}