All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jp.openstandia.connector.atlassian.AtlassianGuardRESTClient Maven / Gradle / Ivy

The newest version!
/*
 *  Copyright Nomura Research Institute, Ltd.
 *
 *  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
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  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 jp.openstandia.connector.atlassian;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import jp.openstandia.connector.util.AbstractRESTClient;
import jp.openstandia.connector.util.QueryHandler;
import okhttp3.OkHttpClient;
import okhttp3.Response;
import org.identityconnectors.common.logging.Log;
import org.identityconnectors.framework.common.exceptions.AlreadyExistsException;
import org.identityconnectors.framework.common.exceptions.ConnectionFailedException;
import org.identityconnectors.framework.common.exceptions.ConnectorIOException;
import org.identityconnectors.framework.common.exceptions.UnknownUidException;
import org.identityconnectors.framework.common.objects.Name;
import org.identityconnectors.framework.common.objects.OperationOptions;
import org.identityconnectors.framework.common.objects.Uid;

import java.io.IOException;
import java.util.*;

import static jp.openstandia.connector.atlassian.AtlassianGuardGroupHandler.GROUP_OBJECT_CLASS;
import static jp.openstandia.connector.atlassian.AtlassianGuardUserHandler.USER_OBJECT_CLASS;

public class AtlassianGuardRESTClient extends AbstractRESTClient {
    private static final Log LOG = Log.getLog(AtlassianGuardRESTClient.class);
    private ErrorHandler ERROR_HANDLER = new AtlassianGuardErrorHandler();

    private String testEndpoint;
    private String userEndpoint;
    private String groupEndpoint;

    @JsonIgnoreProperties(ignoreUnknown = true)
    static class UserListBody {
        public int totalResults;
        public int startIndex;
        public int itemPerPage;
        @JsonProperty("Resources")
        public List resources;
    }

    @JsonIgnoreProperties(ignoreUnknown = true)
    static class GroupListBody {
        public int totalResults;
        public int startIndex;
        public int itemPerPage;
        @JsonProperty("Resources")
        public List resources;
    }

    static class AtlassianGuardErrorHandler implements ErrorHandler {
        @Override
        public boolean inNotAuthenticated(Response response) {
            // {"schemas":["urn:ietf:params:scim:api:messages:2.0:Error"],"status":"401","detail":"UnauthorizedError: Invalid authentication"}
            return response.code() == 401;
        }

        @Override
        public boolean isAlreadyExists(Response response) {
            // {"schemas":["urn:ietf:params:scim:api:messages:2.0:Error"],"status":"409","scimType":"uniqueness"}
            return response.code() == 409;
        }

        @Override
        public boolean isInvalidRequest(Response response) {
            // {"schemas":["urn:ietf:params:scim:api:messages:2.0:Error"],"status":"400","scimType":"invalidSyntax","detail":"body failed validation: body.startIndex should be ≥ `1` or `undefined`, instead was `0`."}
            return response.code() == 400;
        }

        @Override
        public boolean isNotFound(Response response) {
            // {"schemas":["urn:ietf:params:scim:api:messages:2.0:Error"],"status":"404"}
            return response.code() == 404;
        }

        @Override
        public boolean isOk(Response response) {
            return response.code() == 200 || response.code() == 201 || response.code() == 204;
        }

        @Override
        public boolean isServerError(Response response) {
            return response.code() >= 500 && response.code() <= 599;
        }
    }

    public void init(String instanceName, AtlassianGuardConfiguration configuration, OkHttpClient httpClient) {
        super.init(instanceName, configuration, httpClient, ERROR_HANDLER, false, "startIndex", "count");
        this.testEndpoint = configuration.getBaseURL() + "/ServiceProviderConfig";
        this.userEndpoint = configuration.getBaseURL() + "/Users";
        this.groupEndpoint = configuration.getBaseURL() + "/Groups";
    }

    public void test() {
        try (Response response = get(testEndpoint)) {
            if (response.code() != 200) {
                // Something wrong..
                String body = response.body().string();
                throw new ConnectionFailedException(String.format("Failed %s test response. statusCode: %s, body: %s",
                        instanceName,
                        response.code(),
                        body));
            }

            LOG.info("{0} connector's connection test is OK", instanceName);

        } catch (IOException e) {
            throw new ConnectionFailedException(String.format("Cannot connect to %s REST API", instanceName), e);
        }
    }

    // User

    public Uid createUser(AtlassianGuardUserModel newUser) throws AlreadyExistsException {
        AtlassianGuardUserModel created = callCreate(USER_OBJECT_CLASS, userEndpoint, newUser, newUser.userName, (response) -> {
            try {
                return MAPPER.readValue(response.body().byteStream(), AtlassianGuardUserModel.class);
            } catch (IOException e) {
                throw new ConnectorIOException(String.format("Cannot parse %s REST API Response", instanceName), e);
            }
        });
        return new Uid(created.id, created.userName);
    }

    public AtlassianGuardUserModel getUser(Uid uid, OperationOptions options, Set fetchFieldsSet) throws UnknownUidException {
        try (Response response = callRead(USER_OBJECT_CLASS, userEndpoint, uid)) {
            if (response == null) {
                return null;
            }
            AtlassianGuardUserModel user = MAPPER.readValue(response.body().byteStream(), AtlassianGuardUserModel.class);
            return user;

        } catch (IOException e) {
            throw new ConnectorIOException(String.format("Cannot parse %s REST API Response", instanceName), e);
        }
    }

    public AtlassianGuardUserModel getUser(Name name, OperationOptions options, Set fetchFieldsSet) throws UnknownUidException {
        Map params = new HashMap<>();
        params.put("filter", formatFilter("userName eq \"%s\"", name.getNameValue()));

        try (Response response = callSearch(USER_OBJECT_CLASS, userEndpoint, params)) {
            UserListBody list = MAPPER.readValue(response.body().byteStream(), UserListBody.class);
            if (list.resources == null || list.resources.size() != 1) {
                LOG.info("The {0} user is not found. userName={1}", instanceName, name.getNameValue());
                return null;
            }
            return list.resources.get(0);

        } catch (IOException e) {
            throw new ConnectorIOException(String.format("Cannot parse %s REST API Response", instanceName), e);
        }
    }

    private String formatFilter(String filter, String... values) {
        Object[] escaped = Arrays.stream(values)
                .map(v -> v.replace("\"", "\\\""))
                .toArray();
        return String.format(filter, escaped);
    }

    public void patchUser(Uid uid, PatchOperationsModel operations) {
        callPatch(USER_OBJECT_CLASS, userEndpoint + "/" + uid.getUidValue(), uid, operations);
    }

    public void deleteUser(Uid uid) {
        callDelete(USER_OBJECT_CLASS, userEndpoint + "/" + uid.getUidValue(), uid, null);
    }

    public int getUsers(QueryHandler handler, OperationOptions options, Set fetchFieldsSet, int pageSize, int pageOffset) {
        // ConnId starts from 1, 0 means no offset (requested all data)
        if (pageOffset < 1) {
            return getAll(handler, pageSize, (start, size) -> {
                Map params = new HashMap<>();
                params.put(offsetKey, String.valueOf(start));
                params.put(countKey, String.valueOf(size));

                try (Response response = callSearch(USER_OBJECT_CLASS, userEndpoint, params)) {
                    UserListBody list = MAPPER.readValue(response.body().byteStream(), UserListBody.class);
                    return list.resources;

                } catch (IOException e) {
                    throw new ConnectorIOException(String.format("Cannot parse %s REST API Response", instanceName), e);
                }
            });
        }

        // Pagination
        // Notion(SCIM v2.0) starts from 1
        int start = resolveOffset(pageOffset);

        Map params = new HashMap<>();
        params.put(offsetKey, String.valueOf(start));
        params.put(countKey, String.valueOf(pageSize));

        try (Response response = callSearch(USER_OBJECT_CLASS, userEndpoint, params)) {
            UserListBody list = MAPPER.readValue(response.body().byteStream(), UserListBody.class);
            for (AtlassianGuardUserModel user : list.resources) {
                if (!handler.handle(user)) {
                    break;
                }
            }
            return list.totalResults;

        } catch (IOException e) {
            throw new ConnectorIOException(String.format("Cannot parse %s REST API Response", instanceName), e);
        }
    }

    // Group

    public Uid createGroup(AtlassianGuardGroupModel newGroup) throws AlreadyExistsException {
        AtlassianGuardGroupModel created = callCreate(GROUP_OBJECT_CLASS, groupEndpoint, newGroup, newGroup.displayName, (response) -> {
            try {
                return MAPPER.readValue(response.body().byteStream(), AtlassianGuardGroupModel.class);
            } catch (IOException e) {
                throw new ConnectorIOException(String.format("Cannot parse %s REST API Response", instanceName), e);
            }
        });

        return new Uid(created.id, newGroup.displayName);
    }

    public void patchGroup(Uid uid, PatchOperationsModel operations) {
        callPatch(GROUP_OBJECT_CLASS, groupEndpoint + "/" + uid.getUidValue(), uid, operations);
    }

    public AtlassianGuardGroupModel getGroup(Uid uid, OperationOptions options, Set fetchFieldsSet) throws UnknownUidException {
        try (Response response = callRead(GROUP_OBJECT_CLASS, groupEndpoint, uid)) {
            if (response == null) {
                return null;
            }
            AtlassianGuardGroupModel group = MAPPER.readValue(response.body().byteStream(), AtlassianGuardGroupModel.class);
            return group;

        } catch (IOException e) {
            throw new ConnectorIOException(String.format("Cannot parse %s REST API Response", instanceName), e);
        }
    }

    public AtlassianGuardGroupModel getGroup(Name name, OperationOptions options, Set fetchFieldsSet) {
        Map params = new HashMap<>();
        params.put("filter", formatFilter("displayName eq \"%s\"", name.getNameValue()));

        try (Response response = callSearch(GROUP_OBJECT_CLASS, groupEndpoint, params)) {
            GroupListBody list = MAPPER.readValue(response.body().byteStream(), GroupListBody.class);
            if (list.resources == null || list.resources.size() != 1) {
                LOG.info("The {0} group is not found. displayName={1}", instanceName, name.getNameValue());
                return null;
            }
            return list.resources.get(0);

        } catch (IOException e) {
            throw new ConnectorIOException(String.format("Cannot parse %s REST API Response", instanceName), e);
        }
    }

    public int getGroups(QueryHandler handler, OperationOptions options, Set fetchFieldsSet, int pageSize, int pageOffset) {
        // ConnId starts from 1, 0 means no offset (requested all data)
        if (pageOffset < 1) {
            return getAll(handler, pageSize, (start, size) -> {
                Map params = new HashMap<>();
                params.put(offsetKey, String.valueOf(start));
                params.put(countKey, String.valueOf(size));

                try (Response response = callSearch(GROUP_OBJECT_CLASS, groupEndpoint, params)) {
                    GroupListBody list = MAPPER.readValue(response.body().byteStream(), GroupListBody.class);
                    return list.resources;

                } catch (IOException e) {
                    throw new ConnectorIOException(String.format("Cannot parse %s REST API Response", instanceName), e);
                }
            });
        }

        // Pagination
        int start = resolveOffset(pageOffset);

        Map params = new HashMap<>();
        params.put(offsetKey, String.valueOf(start));
        params.put(countKey, String.valueOf(pageSize));

        try (Response response = callSearch(GROUP_OBJECT_CLASS, groupEndpoint, params)) {
            GroupListBody list = MAPPER.readValue(response.body().byteStream(), GroupListBody.class);
            for (AtlassianGuardGroupModel group : list.resources) {
                if (!handler.handle(group)) {
                    break;
                }
            }
            return list.totalResults;

        } catch (IOException e) {
            throw new ConnectorIOException(String.format("Cannot parse %s REST API Response", instanceName), e);
        }
    }

    public void deleteGroup(Uid uid) {
        callDelete(GROUP_OBJECT_CLASS, groupEndpoint + "/" + uid.getUidValue(), uid, null);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy