外部の役割マッピング

Edge for Private Cloud v4.19.01

外部の役割マッピングを使用すると、独自のグループまたは役割を、Apigee Edge で作成された役割ベースのアクセス制御(RBAC)の役割とグループにマッピングできます。この機能は Private Cloud 対応の Edge でのみ使用できます。

前提条件

  • この構成を行うには、Apigee Private Cloud システム管理者であり、グローバル システム管理者の資格情報を持っている必要があります。
  • Apigee Edge Private Cloud インストールのルート ディレクトリを知っている必要があります。デフォルトのルート ディレクトリは /opt です。

ステップバイステップの設定例

外部の役割マッピングを設定するためのステップバイステップの例については、Apigee コミュニティ フォーラムのこちらの記事をご覧ください。

デフォルト構成

外部の役割マッピングは、デフォルトでは無効になっています。

外部の役割マッピングの有効化

外部の役割マッピングを有効にするには、次のようにします。

  1. 次の構成を完了する前に、ExternalRoleMapperServiceV2 インターフェースを実装する Java クラスを作成し、その実装を Management Server クラスパスに含める必要があります。
    /opt/apigee/edge-management-server/lib/thirdparty/

    この実装の詳細については、このドキュメント後半の ExternalRoleMapperImpl サンプル実装についてのセクションをご覧ください。

  2. Apigee Edge Management Server にログインし、管理サーバー プロセスを停止します。
    /opt/apigee/apigee-service/bin/apigee-service edge-management-server stop
  3. テキスト エディタで /opt/apigee/customer/application/management-server.properties を開きます。このファイルが存在しない場合は作成します。
  4. プロパティ ファイルを編集して、次のように設定します。
    # The user store to be used for authentication.
        # Use "externalized.authentication" for LDAP user store.
        # Note that for authorization, we continue to use LDAP.
        # See Enabling external authentication more on enabling external auth.
        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
  5. management-server.properties ファイルを保存します。
  6. management-server.properties を apigee ユーザーが所有していることを確認します。
    chown apigee:apigee /opt/apigee/customer/application/management-server.properties
  7. Management Server を起動します。
    /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. Management Server を再起動します。
    /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 に配置し、さらに 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();
       }

    }