Back to Repositories

Testing Authorization Control Implementation in Conductor OSS

The AuthorizationClientTests suite validates the authorization and access control functionality in the Conductor OSS platform. It tests user management, group permissions, application access, and tag-based authorization using JUnit framework.

Test Coverage Overview

This test suite provides comprehensive coverage of Conductor’s authorization system, including:

  • User management and authentication flows
  • Group permission assignments and inheritance
  • Application access key management
  • Tag-based authorization controls
  • Domain-level permission management
  • Workflow and task access control

Implementation Analysis

The tests utilize JUnit 5 with a systematic approach to authorization testing. Each test method focuses on specific authorization scenarios using the AuthorizationClient interface. The implementation leverages BeforeAll/AfterAll hooks for test setup and cleanup, with thorough assertions validating permission states.

Technical Details

  • Testing Framework: JUnit Jupiter (JUnit 5)
  • Client Library: conductor-java-sdk
  • Test Utilities: ClientTestUtil, Commons
  • Key Components: AuthorizationClient, OrkesMetadataClient
  • Test Data: Dynamic UUID generation for unique test resources

Best Practices Demonstrated

The test suite exemplifies several testing best practices:

  • Proper test isolation with setup/cleanup
  • Comprehensive assertion coverage
  • Meaningful test descriptions using @DisplayName
  • Modular test helper methods
  • Exception handling validation
  • Systematic permission verification

conductor-oss/conductor

conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/AuthorizationClientTests.java

            
/*
 * Copyright 2022 Conductor Authors.
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 */
package io.orkes.conductor.client.http;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import com.netflix.conductor.client.exception.ConductorClientException;
import com.netflix.conductor.common.metadata.workflow.WorkflowDef;
import com.netflix.conductor.common.metadata.workflow.WorkflowTask;

import io.orkes.conductor.client.AuthorizationClient;
import io.orkes.conductor.client.OrkesClients;
import io.orkes.conductor.client.model.AccessKeyResponse;
import io.orkes.conductor.client.model.AuthorizationRequest;
import io.orkes.conductor.client.model.ConductorApplication;
import io.orkes.conductor.client.model.ConductorUser;
import io.orkes.conductor.client.model.CreateAccessKeyResponse;
import io.orkes.conductor.client.model.CreateOrUpdateApplicationRequest;
import io.orkes.conductor.client.model.Group;
import io.orkes.conductor.client.model.SubjectRef;
import io.orkes.conductor.client.model.TagObject;
import io.orkes.conductor.client.model.TargetRef;
import io.orkes.conductor.client.model.TargetRef.TypeEnum;
import io.orkes.conductor.client.model.UpsertGroupRequest;
import io.orkes.conductor.client.model.UpsertGroupRequest.RolesEnum;
import io.orkes.conductor.client.model.UpsertUserRequest;
import io.orkes.conductor.client.util.ClientTestUtil;
import io.orkes.conductor.client.util.Commons;

public class AuthorizationClientTests {
    private static AuthorizationClient authorizationClient;
    private static String applicationId;
    private static OrkesMetadataClient metadataClient;

    @BeforeAll
    public static void setup() {
        OrkesClients orkesClients = ClientTestUtil.getOrkesClients();
        authorizationClient = orkesClients.getAuthorizationClient();
        metadataClient = orkesClients.getMetadataClient();
        CreateOrUpdateApplicationRequest request = new CreateOrUpdateApplicationRequest();
        request.setName("test-" + UUID.randomUUID());
        ConductorApplication app = authorizationClient.createApplication(request);
        applicationId = app.getId();
    }

    @AfterAll
    public static void cleanup() {
        if(applicationId != null) {
            authorizationClient.deleteApplication(applicationId);
        }
    }

    @Test
    @DisplayName("auto assign group permission on workflow creation by any group member")
    public void autoAssignWorkflowPermissions() {
        giveApplicationPermissions(applicationId);
        Group group = authorizationClient.upsertGroup(getUpsertGroupRequest(), "sdk-test-group");
        validateGroupPermissions(group.getId());
    }

    @Test
    void testUser() {
        ConductorUser user =
                authorizationClient.upsertUser(getUpserUserRequest(), Commons.USER_EMAIL);
        ConductorUser receivedUser = authorizationClient.getUser(Commons.USER_EMAIL);
        Assertions.assertEquals(user.getName(), receivedUser.getName());
        Assertions.assertEquals(user.getGroups().get(0).getId(), receivedUser.getGroups().get(0).getId());
        Assertions.assertEquals(user.getRoles().get(0).getName(), receivedUser.getRoles().get(0).getName());
        authorizationClient.sendInviteEmail(user.getId());
        Group group = authorizationClient.upsertGroup(getUpsertGroupRequest(), Commons.GROUP_ID);
        Assertions.assertNotNull(group);
        authorizationClient.removeUserFromGroup(Commons.GROUP_ID, user.getId());
        authorizationClient.removePermissions(getAuthorizationRequest());
    }

    @Test
    void testGroup() {
        UpsertGroupRequest request = new UpsertGroupRequest();

        // Default Access for the group. When specified, any new workflow or task
        // created by the
        // members of this group
        // get this default permission inside the group.
        Map<String, List<String>> defaultAccess = new HashMap<>();

        // Grant READ access to the members of the group for any new workflow created by
        // a member of
        // this group
        defaultAccess.put(TypeEnum.WORKFLOW_DEF.getValue(), List.of("READ"));

        // Grant EXECUTE access to the members of the group for any new task created by
        // a member of
        // this group
        defaultAccess.put(TypeEnum.TASK_DEF.getValue(), List.of("EXECUTE"));
        request.setDefaultAccess(defaultAccess);

        request.setDescription("Example group created for testing");
        request.setRoles(Arrays.asList(UpsertGroupRequest.RolesEnum.USER));

        Group group = authorizationClient.upsertGroup(request, Commons.GROUP_ID);
        Assertions.assertNotNull(group);
        Group found = authorizationClient.getGroup(Commons.GROUP_ID);
        Assertions.assertNotNull(found);
        Assertions.assertEquals(group.getId(), found.getId());
        Assertions.assertEquals(group.getDefaultAccess().keySet(), found.getDefaultAccess().keySet());
    }

    @Test
    void testApplication() {
        CreateOrUpdateApplicationRequest request = new CreateOrUpdateApplicationRequest();
        request.setName("Test Application for the testing");

        // WARNING: Application Name is not a UNIQUE value and if called multiple times,
        // it will
        // create a new application
        ConductorApplication application = authorizationClient.createApplication(request);
        Assertions.assertNotNull(application);
        Assertions.assertNotNull(application.getId());

        // Get the list of applications
        List<ConductorApplication> apps = authorizationClient.listApplications();
        Assertions.assertNotNull(apps);
        long found =
                apps.stream()
                        .map(ConductorApplication::getId)
                        .filter(id -> id.equals(application.getId()))
                        .count();
        Assertions.assertEquals(1, found);

        // Create new access key
        CreateAccessKeyResponse accessKey =
                authorizationClient.createAccessKey(application.getId());
        List<AccessKeyResponse> accessKeyResponses =
                authorizationClient.getAccessKeys(application.getId());
        Assertions.assertEquals(1, accessKeyResponses.size());
        authorizationClient.toggleAccessKeyStatus(application.getId(), accessKey.getId());
        authorizationClient.deleteAccessKey(application.getId(), accessKey.getId());
        authorizationClient.setApplicationTags(getTagObject(), application.getId());
        Assertions.assertEquals(getTagObject(), authorizationClient.getApplicationTags(application.getId()));
        authorizationClient.deleteApplicationTags(getTagObject(), application.getId());
        Assertions.assertEquals(0, authorizationClient.getApplicationTags(application.getId()).size());
        accessKeyResponses = authorizationClient.getAccessKeys(application.getId());
        Assertions.assertEquals(0, accessKeyResponses.size());

        authorizationClient.removeRoleFromApplicationUser(
                application.getId(), RolesEnum.ADMIN.getValue());

        String newName = "ansdjansdjna";
        authorizationClient.updateApplication(
                new CreateOrUpdateApplicationRequest().name(newName), application.getId());
        Assertions.assertEquals(newName, authorizationClient.getApplication(application.getId()).getName());

        authorizationClient.deleteApplication(application.getId());
    }

    @Test
    void testGrantPermissionsToGroup() {
        AuthorizationRequest request = new AuthorizationRequest();
        request.access(Arrays.asList(AuthorizationRequest.AccessEnum.READ));
        SubjectRef subject = new SubjectRef();
        subject.setId("worker-test-group31dfe7a4-bd85-4ccc-9571-7c0e018ebc32");
        subject.setType(SubjectRef.TypeEnum.GROUP);
        request.setSubject(subject);
        TargetRef target = new TargetRef();
        target.setId("Test_032");
        target.setType(TargetRef.TypeEnum.WORKFLOW_DEF);
        request.setTarget(target);
        authorizationClient.grantPermissions(request);
    }

    @Test
    void testGrantPermissionsToDomain() {
        AuthorizationRequest request = new AuthorizationRequest();
        request.access(Arrays.asList(AuthorizationRequest.AccessEnum.EXECUTE));
        SubjectRef subject = new SubjectRef();
        subject.setId("[email protected]");
        subject.setType(SubjectRef.TypeEnum.USER);
        request.setSubject(subject);
        TargetRef target = new TargetRef();
        target.setId("my-domain");
        target.setType(TargetRef.TypeEnum.DOMAIN);
        request.setTarget(target);
        authorizationClient.grantPermissions(request);
    }

    @Test
    @DisplayName("tag a workflows and task")
    public void tagWorkflowsAndTasks() {
        registerWorkflow();
        TagObject tagObject = new TagObject();
        tagObject.setType(TagObject.TypeEnum.METADATA);
        tagObject.setKey("a");
        tagObject.setValue("b");
        metadataClient.addTaskTag(tagObject, Commons.TASK_NAME);
        metadataClient.addWorkflowTag(tagObject, Commons.WORKFLOW_NAME);
    }

    public void registerWorkflow() {
        WorkflowDef workflowDef = new WorkflowDef();
        workflowDef.setName(Commons.WORKFLOW_NAME);
        workflowDef.setVersion(Commons.WORKFLOW_VERSION);
        workflowDef.setOwnerEmail(Commons.OWNER_EMAIL);
        WorkflowTask workflowTask = new WorkflowTask();
        workflowTask.setName(Commons.TASK_NAME);
        workflowTask.setTaskReferenceName(Commons.TASK_NAME);
        workflowDef.setTasks(List.of(workflowTask));
        metadataClient.updateWorkflowDefs(Arrays.asList(workflowDef));
    }

    @Test
    void testGrantPermissionsToTag() {
        authorizationClient.grantPermissions(getAuthorizationRequest());
    }

    @Test
    void testMethods() {
        try {
            authorizationClient.deleteUser(Commons.USER_EMAIL);
        } catch (ConductorClientException e) {
            if (e.getStatus() != 404) {
                throw e;
            }
        }
        authorizationClient.upsertUser(getUpserUserRequest(), Commons.USER_EMAIL);
        List<ConductorUser> users = authorizationClient.listUsers(false);
        Assertions.assertFalse(users.isEmpty());
        users = authorizationClient.listUsers(true);
        Assertions.assertFalse(users.isEmpty());
        try {
            authorizationClient.deleteGroup(Commons.GROUP_ID);
        } catch (ConductorClientException e) {
            if (e.getStatus() != 404) {
                throw e;
            }
        }
        authorizationClient.upsertGroup(getUpsertGroupRequest(), Commons.GROUP_ID);
        List<Group> groups = authorizationClient.listGroups();
        Assertions.assertFalse(groups.isEmpty());
        authorizationClient.addUserToGroup(Commons.GROUP_ID, Commons.USER_EMAIL);
        boolean found = false;
        for (ConductorUser user : authorizationClient.getUsersInGroup(Commons.GROUP_ID)) {
            if (user.getName().equals(Commons.USER_NAME)) {
                found = true;
            }
        }
        Assertions.assertTrue(found);
        authorizationClient.getPermissions("APPLICATION", applicationId);
        Assertions.assertEquals(authorizationClient.getApplication(applicationId).getId(), applicationId);
        Assertions.assertTrue(
                authorizationClient
                        .getGrantedPermissionsForGroup(Commons.GROUP_ID)
                        .getGrantedAccess()
                        .isEmpty());
        // The user is added just now so it should not have any access.
        Assertions.assertTrue(
                authorizationClient
                        .getGrantedPermissionsForUser(Commons.USER_EMAIL)
                        .getGrantedAccess()
                        .isEmpty());
    }

    void giveApplicationPermissions(String applicationId) {
        authorizationClient.addRoleToApplicationUser(applicationId, RolesEnum.ADMIN.getValue());
    }

    void validateGroupPermissions(String id) {
        Group group = authorizationClient.getGroup(id);
        for (Map.Entry<String, List<String>> entry : group.getDefaultAccess().entrySet()) {
            List<String> expectedList = new ArrayList<>(getAccessListAll());
            List<String> actualList = new ArrayList<>(entry.getValue());
            Collections.sort(expectedList);
            Collections.sort(actualList);
            Assertions.assertEquals(expectedList, actualList);
        }
    }

    UpsertGroupRequest getUpsertGroupRequest() {
        return new UpsertGroupRequest()
                .defaultAccess(
                        Map.of(
                                TypeEnum.WORKFLOW_DEF.getValue(), getAccessListAll(),
                                TypeEnum.TASK_DEF.getValue(), getAccessListAll()))
                .description("Group used for SDK testing")
                .roles(List.of(RolesEnum.ADMIN));
    }

    UpsertUserRequest getUpserUserRequest() {
        UpsertUserRequest request = new UpsertUserRequest();
        request.setName(Commons.USER_NAME);
        request.setGroups(List.of(Commons.GROUP_ID));
        request.setRoles(List.of(UpsertUserRequest.RolesEnum.USER));
        return request;
    }



    List<String> getAccessListAll() {
        return List.of("CREATE", "READ", "UPDATE", "EXECUTE", "DELETE");
    }

    AuthorizationRequest getAuthorizationRequest() {
        AuthorizationRequest request = new AuthorizationRequest();
        request.access(Arrays.asList(AuthorizationRequest.AccessEnum.READ));
        SubjectRef subject = new SubjectRef();
        subject.setId("worker-test-group31dfe7a4-bd85-4ccc-9571-7c0e018ebc32");
        subject.setType(SubjectRef.TypeEnum.GROUP);
        request.setSubject(subject);
        TargetRef target = new TargetRef();
        target.setId("org:accounting");
        target.setType(TargetRef.TypeEnum.TAG);
        request.setTarget(target);
        return request;
    }

    private List<TagObject> getTagObject() {
        TagObject tagObject = new TagObject();
        tagObject.setKey("department");
        tagObject.setValue("accounts");
        return List.of(tagObject);
    }
}