외부 역할 매핑

Private Cloud용 Edge v4.18.01

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

새로운 기능

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

  • 액세스 권한이 있어야 하는 사용자로 인증할 때 인증 403 금지됨 응답을 받는 문제가 수정되었습니다.
  • 이제 외부 역할 매핑에서 X-Apigee-Current-User 헤더가 지원됩니다. 이제 적절한 액세스 권한을 가진 사용자 (시스템 관리자)가 다른 사용자에게 할당된 역할을 볼 수 있습니다.

기본 요건

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

단계별 설정 예

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

기본 구성

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

외부 역할 매핑 사용 설정

  1. 다음 구성을 완료하려면 먼저 ExternalRoleMapperServiceV2 인터페이스를 구현하고 관리 서버 클래스 경로에 구현을 포함하는 자바 클래스를 만들어야 합니다.

    /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'을 사용합니다.
    # 승인에는 LDAP가 계속 사용됩니다.
    # 외부 인증 사용 설정에 대한 자세한 내용은 외부 인증 사용 설정을 참조하세요.
    conf_security_authentication.user.store=externalized.authentication

    #외부 승인 역할 매퍼를 사용 설정합니다.
    conf_security_externalized.authentication.role.mapper.enabled=true conf_security_externalized.authentication.role.mapper.implementation.class=
    com.customer.authorization.impl.ExternalRoleMapperImpl

    중요:
    원하는 패키지 이름과 패키지 이름(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();
   }

}