loadKeySpec(final byte[] privateKey) {
- Matcher isPEM = PKCS1_PEM_KEY_PATTERN.matcher(new String(privateKey));
- if (!isPEM.matches()) {
- isPEM = PKCS1_PEM_KEY_ONE_LINE_PATTERN.matcher(new String(privateKey));
- if (!isPEM.matches()) {
- return Optional.empty();
- }
- }
-
- byte[] pkcs1Key = Base64.getMimeDecoder().decode(isPEM.group(1));
- byte[] pkcs8Key = toPkcs8(pkcs1Key);
- final PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(pkcs8Key);
- return Optional.of(keySpec);
- }
-
- /**
- * Convert a PKCS#1 key to a PKCS#8 key.
- *
- * The Github app key comes in PKCS#1 format, while the Java security utilities only natively
- * understand PKCS#8. Fortunately, we can convert between the two by adding the PKCS#8 headers
- * manually.
- *
- *
Adapted from code in https://github.com/Mastercard/client-encryption-java
- */
- @SuppressWarnings("checkstyle:magicnumber")
- private static byte[] toPkcs8(final byte[] pkcs1Bytes) {
- final int pkcs1Length = pkcs1Bytes.length;
- final int totalLength = pkcs1Length + 22;
- byte[] pkcs8Header = new byte[]{
- 0x30, (byte) 0x82, (byte) ((totalLength >> 8) & 0xff), (byte) (totalLength & 0xff), // Sequence + total length
- 0x2, 0x1, 0x0, // Integer (0)
- 0x30, 0xD, 0x6, 0x9, 0x2A, (byte) 0x86, 0x48, (byte) 0x86, (byte) 0xF7, 0xD, 0x1, 0x1, 0x1, 0x5, 0x0, // Sequence: 1.2.840.113549.1.1.1, NULL
- 0x4, (byte) 0x82, (byte) ((pkcs1Length >> 8) & 0xff), (byte) (pkcs1Length & 0xff) // Octet string + length
- };
-
- byte[] pkcs8bytes = new byte[pkcs8Header.length + pkcs1Bytes.length];
- System.arraycopy(pkcs8Header, 0, pkcs8bytes, 0, pkcs8Header.length);
- System.arraycopy(pkcs1Bytes, 0, pkcs8bytes, pkcs8Header.length, pkcs1Bytes.length);
- return pkcs8bytes;
- }
-}
diff --git a/src/main/java/jp/openstandia/connector/github/AbstractGitHubConnector.java b/src/main/java/jp/openstandia/connector/github/AbstractGitHubConnector.java
index 3978b84..b58451b 100644
--- a/src/main/java/jp/openstandia/connector/github/AbstractGitHubConnector.java
+++ b/src/main/java/jp/openstandia/connector/github/AbstractGitHubConnector.java
@@ -105,7 +105,7 @@ private ObjectHandler getSchemaHandler(ObjectClass objectClass) {
if (handler == null) {
throw new InvalidAttributeValueException("Unsupported object class " + objectClass);
}
-
+
handler.setInstanceName(instanceName);
return handler;
@@ -274,4 +274,4 @@ protected ConnectorException processRuntimeException(RuntimeException e) {
}
return new ConnectorException(e);
}
-}
+}
\ No newline at end of file
diff --git a/src/main/java/jp/openstandia/connector/github/GitHubClient.java b/src/main/java/jp/openstandia/connector/github/GitHubClient.java
index 14ff6cf..c6d45bc 100644
--- a/src/main/java/jp/openstandia/connector/github/GitHubClient.java
+++ b/src/main/java/jp/openstandia/connector/github/GitHubClient.java
@@ -27,10 +27,7 @@
import org.identityconnectors.framework.common.objects.OperationOptions;
import org.identityconnectors.framework.common.objects.ResultsHandler;
import org.identityconnectors.framework.common.objects.Uid;
-import org.kohsuke.github.SCIMEMUGroup;
-import org.kohsuke.github.SCIMEMUUser;
-import org.kohsuke.github.SCIMPatchOperations;
-import org.kohsuke.github.SCIMUser;
+import org.kohsuke.github.*;
import java.net.InetSocketAddress;
import java.net.Proxy;
@@ -82,77 +79,6 @@ default OkHttpClient createClient(AbstractGitHubConfiguration configuration) {
void close();
- // User
-
- default Uid createUser(T schema, SCIMUser scimUser) throws AlreadyExistsException {
- throw new UnsupportedOperationException();
- }
-
- default String updateUser(T schema, Uid uid, String scimUserName, String scimEmail, String scimGivenName, String scimFamilyName, String login, OperationOptions options) throws UnknownUidException {
- throw new UnsupportedOperationException();
- }
-
- default void deleteUser(T schema, Uid uid, OperationOptions options) throws UnknownUidException {
- throw new UnsupportedOperationException();
- }
-
- default void getUsers(T schema, ResultsHandler handler, OperationOptions options, Set attributesToGet, boolean allowPartialAttributeValues, int queryPageSize) {
- throw new UnsupportedOperationException();
- }
-
- default void getUser(T schema, Uid uid, ResultsHandler handler, OperationOptions options, Set attributesToGet, boolean allowPartialAttributeValues, int queryPageSize) {
- throw new UnsupportedOperationException();
- }
-
- default void getUser(T schema, Name name, ResultsHandler handler, OperationOptions options, Set attributesToGet, boolean allowPartialAttributeValues, int queryPageSize) {
- throw new UnsupportedOperationException();
- }
-
- // Team
-
- default List getTeamIdsByUsername(String userLogin, int pageSize) {
- throw new UnsupportedOperationException();
- }
-
- default boolean isOrganizationMember(String userLogin) {
- throw new UnsupportedOperationException();
- }
-
- default void assignOrganizationRole(String userLogin, String organizationRole) {
- throw new UnsupportedOperationException();
- }
-
- default void assignTeams(String login, String role, Collection teams) {
- throw new UnsupportedOperationException();
- }
-
- default void unassignTeams(String login, Collection teams) {
- throw new UnsupportedOperationException();
- }
-
- default Uid createTeam(T schema, String teamName, String description, String privacy, Long parentTeamDatabaseId) throws AlreadyExistsException {
- throw new UnsupportedOperationException();
- }
-
- default Uid updateTeam(T schema, Uid uid, String teamName, String description, String privacy, Long parentTeamId, boolean clearParent, OperationOptions options) throws UnknownUidException {
- throw new UnsupportedOperationException();
- }
-
- default void deleteTeam(T schema, Uid uid, OperationOptions options) throws UnknownUidException {
- throw new UnsupportedOperationException();
- }
-
- default void getTeams(T schema, ResultsHandler handler, OperationOptions options, Set attributesToGet, boolean allowPartialAttributeValues, int queryPageSize) {
- throw new UnsupportedOperationException();
- }
-
- default void getTeam(T schema, Uid uid, ResultsHandler handler, OperationOptions options, Set attributesToGet, boolean allowPartialAttributeValues, int queryPageSize) {
- throw new UnsupportedOperationException();
- }
-
- default void getTeam(T schema, Name name, ResultsHandler handler, OperationOptions options, Set attributesToGet, boolean allowPartialAttributeValues, int queryPageSize) {
- throw new UnsupportedOperationException();
- }
// EMU User
@@ -205,5 +131,19 @@ default SCIMEMUGroup getEMUGroup(Uid uid, OperationOptions options, Set
default SCIMEMUGroup getEMUGroup(Name name, OperationOptions options, Set attributesToGet) {
throw new UnsupportedOperationException();
}
+
+ // Copilot Seats
+
+ default GitHubCopilotSeat getCopilotSeat(Uid uid, OperationOptions options, Set attributesToGet) {
+ throw new UnsupportedOperationException();
+ }
+
+ default GitHubCopilotSeat getCopilotSeat(Name name, OperationOptions options, Set attributesToGet) {
+ throw new UnsupportedOperationException();
+ }
+
+ default int getCopilotSeats(QueryHandler handler, OperationOptions options, Set fetchFieldsSet, int pageSize, int pageOffset) {
+ throw new UnsupportedOperationException();
+ }
}
diff --git a/src/main/java/jp/openstandia/connector/github/GitHubConfiguration.java b/src/main/java/jp/openstandia/connector/github/GitHubConfiguration.java
deleted file mode 100644
index c7994c7..0000000
--- a/src/main/java/jp/openstandia/connector/github/GitHubConfiguration.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * 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.github;
-
-import org.identityconnectors.common.security.GuardedString;
-import org.identityconnectors.framework.spi.ConfigurationProperty;
-
-/**
- * Connector Configuration implementation for GitHub connector.
- *
- * @author Hiroyuki Wada
- */
-public class GitHubConfiguration extends AbstractGitHubConfiguration {
-
- private GuardedString privateKey;
- private String appId;
- private long installationId;
- private String organizationName;
-
- @ConfigurationProperty(
- order = 1,
- displayMessageKey = "Private Key (PEM)",
- helpMessageKey = "Set Private Key with PEM format for GitHub API.",
- required = true,
- confidential = true)
- public GuardedString getPrivateKey() {
- return privateKey;
- }
-
- public void setPrivateKey(GuardedString privateKey) {
- this.privateKey = privateKey;
- }
-
- @ConfigurationProperty(
- order = 2,
- displayMessageKey = "App ID",
- helpMessageKey = "Set App ID for GitHub.",
- required = true,
- confidential = false)
- public String getAppId() {
- return appId;
- }
-
- public void setAppId(String appId) {
- this.appId = appId;
- }
-
- @ConfigurationProperty(
- order = 3,
- displayMessageKey = "Installation ID",
- helpMessageKey = "Set Installation ID for GitHub.",
- required = true,
- confidential = true)
- public long getInstallationId() {
- return installationId;
- }
-
- public void setInstallationId(long installationId) {
- this.installationId = installationId;
- }
-
- @ConfigurationProperty(
- order = 4,
- displayMessageKey = "Organization Name",
- helpMessageKey = "Set GitHub organization name.",
- required = true,
- confidential = false)
- public String getOrganizationName() {
- return organizationName;
- }
-
- public void setOrganizationName(String organizationName) {
- this.organizationName = organizationName;
- }
-
- @Override
- public void validate() {
- }
-}
diff --git a/src/main/java/jp/openstandia/connector/github/GitHubConnector.java b/src/main/java/jp/openstandia/connector/github/GitHubConnector.java
deleted file mode 100644
index 832baa6..0000000
--- a/src/main/java/jp/openstandia/connector/github/GitHubConnector.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * 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.github;
-
-import jp.openstandia.connector.github.rest.GitHubRESTClient;
-import org.identityconnectors.common.logging.Log;
-import org.identityconnectors.framework.common.exceptions.InvalidAttributeValueException;
-import org.identityconnectors.framework.common.objects.ObjectClass;
-import org.identityconnectors.framework.spi.ConnectorClass;
-
-import static jp.openstandia.connector.github.GitHubTeamHandler.TEAM_OBJECT_CLASS;
-import static jp.openstandia.connector.github.GitHubUserHandler.USER_OBJECT_CLASS;
-
-/**
- * Connector implementation for GitHub connector.
- *
- * @author Hiroyuki Wada
- */
-@ConnectorClass(configurationClass = GitHubConfiguration.class, displayNameKey = "NRI OpenStandia GitHub Connector")
-public class GitHubConnector extends AbstractGitHubConnector {
-
- private static final Log LOG = Log.getLog(GitHubConnector.class);
-
- @Override
- protected GitHubClient newClient(GitHubConfiguration configuration) {
- return new GitHubRESTClient(configuration);
- }
-
- @Override
- protected GitHubSchema newGitHubSchema(GitHubConfiguration configuration, GitHubClient client) {
- return new GitHubSchema(configuration, client);
- }
-}
diff --git a/src/main/java/jp/openstandia/connector/github/GitHubCopilotSeatHandler.java b/src/main/java/jp/openstandia/connector/github/GitHubCopilotSeatHandler.java
new file mode 100644
index 0000000..71f376b
--- /dev/null
+++ b/src/main/java/jp/openstandia/connector/github/GitHubCopilotSeatHandler.java
@@ -0,0 +1,195 @@
+package jp.openstandia.connector.github;
+
+import jp.openstandia.connector.util.SchemaDefinition;
+import org.identityconnectors.common.logging.Log;
+import org.identityconnectors.framework.common.objects.*;
+import org.kohsuke.github.GitHubCopilotSeat;
+import org.kohsuke.github.SCIMPatchOperations;
+
+import java.util.Set;
+
+import static jp.openstandia.connector.util.Utils.toZoneDateTime;
+import static jp.openstandia.connector.util.Utils.toZoneDateTimeForISO8601OffsetDateTime;
+import static org.identityconnectors.framework.common.objects.AttributeInfo.Flags.*;
+
+public class GitHubCopilotSeatHandler extends AbstractGitHubEMUHandler {
+
+ public static final ObjectClass SEAT_OBJECT_CLASS = new ObjectClass("GitHubCopilotSeat");
+
+ private static final Log LOGGER = Log.getLog(GitHubCopilotSeatHandler.class);
+
+ public GitHubCopilotSeatHandler(GitHubEMUConfiguration configuration, GitHubClient client,
+ GitHubEMUSchema schema, SchemaDefinition schemaDefinition) {
+ super(configuration, client, schema, schemaDefinition);
+ }
+
+ public static SchemaDefinition.Builder createSchema(AbstractGitHubConfiguration configuration, GitHubClient client) {
+ SchemaDefinition.Builder sb
+ = SchemaDefinition.newBuilder(SEAT_OBJECT_CLASS, GitHubCopilotSeat.class, SCIMPatchOperations.class, GitHubCopilotSeat.class);
+
+ // __UID__
+ // The id for the seat. Must be unique and unchangeable.
+ sb.addUid("id",
+ SchemaDefinition.Types.UUID,
+ null,
+ (source) -> source.assignee.id,
+ "id",
+ NOT_CREATABLE, NOT_UPDATEABLE
+ );
+
+ // code (__NAME__)
+ // The name for the seat. Must be unique and changeable.
+ sb.addName("displayName",
+ SchemaDefinition.Types.STRING_CASE_IGNORE,
+ (source, dest) -> dest.assignee.login = source,
+ (source, dest) -> dest.replace("displayName", source),
+ (source) -> source.assignee.login,
+ null,
+ REQUIRED
+ );
+
+ // Metadata (readonly)
+ sb.add("created_at",
+ SchemaDefinition.Types.DATETIME,
+ null,
+ (source) -> source.created_at != null ? toZoneDateTimeForISO8601OffsetDateTime(source.created_at) : null,
+ null,
+ NOT_CREATABLE, NOT_UPDATEABLE
+ );
+
+ sb.add("last_authenticated_at",
+ SchemaDefinition.Types.DATETIME,
+ null,
+ (source) -> source.last_authenticated_at != null ? toZoneDateTimeForISO8601OffsetDateTime(source.last_authenticated_at) : null,
+ null,
+ NOT_CREATABLE, NOT_UPDATEABLE
+ );
+
+ sb.add("updated_at",
+ SchemaDefinition.Types.DATETIME,
+ null,
+ (source) -> source.updated_at != null ? toZoneDateTimeForISO8601OffsetDateTime(source.updated_at) : null,
+ null,
+ NOT_CREATABLE, NOT_UPDATEABLE
+ );
+
+ sb.add("last_activity_at",
+ SchemaDefinition.Types.DATETIME,
+ null,
+ (source) -> source.last_activity_at != null ? toZoneDateTimeForISO8601OffsetDateTime(source.last_activity_at) : null,
+ null,
+ NOT_CREATABLE, NOT_UPDATEABLE
+ );
+
+ sb.add("pending_cancellation_date",
+ SchemaDefinition.Types.DATETIME,
+ null,
+ (source) -> source.pending_cancellation_date != null ? toZoneDateTime(source.pending_cancellation_date) : null,
+ null,
+ NOT_CREATABLE, NOT_UPDATEABLE
+ );
+
+ sb.add("last_activity_editor",
+ SchemaDefinition.Types.STRING,
+ (source, dest) -> {
+ dest.last_activity_editor = source;
+ },
+ (source, dest) -> dest.replace("last_activity_editor", source),
+ (source) -> source.last_activity_editor,
+ null
+ );
+
+ sb.add("plan_type",
+ SchemaDefinition.Types.STRING,
+ (source, dest) -> {
+ dest.plan_type = source;
+ },
+ (source, dest) -> dest.replace("plan_type", source),
+ (source) -> source.plan_type,
+ null
+ );
+
+ sb.add("assignee.type",
+ SchemaDefinition.Types.STRING,
+ (source, dest) -> {
+ dest.assignee.type = source;
+ },
+ (source, dest) -> dest.replace("assignee.type", source),
+ (source) -> source.assignee.type,
+ null
+ );
+
+ sb.add("assigning_team.slug",
+ SchemaDefinition.Types.STRING,
+ (source, dest) -> {
+ if (source != null) {
+ dest.assigning_team.slug = source;
+ }
+ },
+ (source, dest) -> dest.replace("assigning_team.slug", source),
+ (source) -> source != null && source.assigning_team != null ? source.assigning_team.slug : null,
+ null
+ );
+
+ LOGGER.ok("The constructed GitHub EMU Seat schema");
+
+ return sb;
+ }
+
+ @Override
+ public Uid create(Set attributes) {
+ return null;
+ }
+
+ @Override
+ public Set updateDelta(Uid uid, Set modifications, OperationOptions options) {
+ return Set.of();
+ }
+
+ @Override
+ public void delete(Uid uid, OperationOptions options) {
+ }
+
+ @Override
+ public int getByUid(Uid uid, ResultsHandler resultsHandler, OperationOptions options, Set returnAttributesSet, Set fetchFieldsSet, boolean allowPartialAttributeValues, int pageSize, int pageOffset) {
+ GitHubCopilotSeat seat = client.getCopilotSeat(uid, options, fetchFieldsSet);
+
+ if (seat != null) {
+ resultsHandler.handle(toConnectorObject(schemaDefinition, seat, returnAttributesSet, allowPartialAttributeValues));
+ return 1;
+ }
+ return 0;
+ }
+
+ @Override
+ public int getByName(Name name, ResultsHandler resultsHandler, OperationOptions options, Set returnAttributesSet, Set fetchFieldsSet, boolean allowPartialAttributeValues, int pageSize, int pageOffset) {
+ GitHubCopilotSeat seat = client.getCopilotSeat(name, options, fetchFieldsSet);
+
+ if (seat != null) {
+ resultsHandler.handle(toConnectorObject(schemaDefinition, seat, returnAttributesSet, allowPartialAttributeValues));
+ return 1;
+ }
+ return 0;
+ }
+
+ @Override
+ public int getByMembers(Attribute attribute, ResultsHandler resultsHandler, OperationOptions options, Set returnAttributesSet, Set fetchFieldSet, boolean allowPartialAttributeValues, int pageSize, int pageOffset) {
+ return super.getByMembers(attribute, resultsHandler, options, returnAttributesSet, fetchFieldSet, allowPartialAttributeValues, pageSize, pageOffset);
+ }
+
+ @Override
+ public int getAll(ResultsHandler resultsHandler, OperationOptions options, Set returnAttributesSet, Set fetchFieldsSet, boolean allowPartialAttributeValues, int pageSize, int pageOffset) {
+ return client.getCopilotSeats((s) -> resultsHandler.handle(toConnectorObject(schemaDefinition, s, returnAttributesSet, allowPartialAttributeValues)),
+ options, fetchFieldsSet, pageSize, pageOffset);
+ }
+
+ @Override
+ public void query(GitHubFilter filter, ResultsHandler resultsHandler, OperationOptions options) {
+ super.query(filter, resultsHandler, options);
+ }
+
+ @Override
+ public ConnectorObject toConnectorObject(SchemaDefinition schema, T user, Set returnAttributesSet, boolean allowPartialAttributeValues) {
+ return super.toConnectorObject(schema, user, returnAttributesSet, allowPartialAttributeValues);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/jp/openstandia/connector/github/GitHubEMUConnector.java b/src/main/java/jp/openstandia/connector/github/GitHubEMUConnector.java
index 89dc55f..0c5cbea 100644
--- a/src/main/java/jp/openstandia/connector/github/GitHubEMUConnector.java
+++ b/src/main/java/jp/openstandia/connector/github/GitHubEMUConnector.java
@@ -17,12 +17,8 @@
import jp.openstandia.connector.github.rest.GitHubEMURESTClient;
import org.identityconnectors.common.logging.Log;
-import org.identityconnectors.framework.common.exceptions.InvalidAttributeValueException;
-import org.identityconnectors.framework.common.objects.ObjectClass;
import org.identityconnectors.framework.spi.ConnectorClass;
-import static jp.openstandia.connector.github.GitHubEMUGroupHandler.GROUP_OBJECT_CLASS;
-import static jp.openstandia.connector.github.GitHubUserHandler.USER_OBJECT_CLASS;
/**
* Connector implementation for GitHub EMU connector.
diff --git a/src/main/java/jp/openstandia/connector/github/GitHubEMUGroupHandler.java b/src/main/java/jp/openstandia/connector/github/GitHubEMUGroupHandler.java
index 35f6584..72d32f7 100644
--- a/src/main/java/jp/openstandia/connector/github/GitHubEMUGroupHandler.java
+++ b/src/main/java/jp/openstandia/connector/github/GitHubEMUGroupHandler.java
@@ -117,7 +117,7 @@ public static SchemaDefinition.Builder createSchema(AbstractGitHubConfiguration
NOT_CREATABLE, NOT_UPDATEABLE
);
- LOGGER.ok("The constructed GitHub EMU User schema");
+ LOGGER.ok("The constructed GitHub EMU Group schema");
return sb;
}
diff --git a/src/main/java/jp/openstandia/connector/github/GitHubEMUSchema.java b/src/main/java/jp/openstandia/connector/github/GitHubEMUSchema.java
index ad65c50..269baae 100644
--- a/src/main/java/jp/openstandia/connector/github/GitHubEMUSchema.java
+++ b/src/main/java/jp/openstandia/connector/github/GitHubEMUSchema.java
@@ -32,12 +32,14 @@ public class GitHubEMUSchema extends AbstractGitHubSchema client) {
super(configuration, client);
- SchemaBuilder schemaBuilder = new SchemaBuilder(GitHubConnector.class);
+ SchemaBuilder schemaBuilder = new SchemaBuilder(GitHubEMUConnector.class);
buildSchema(schemaBuilder, GitHubEMUUserHandler.createSchema(configuration, client).build(),
(schema) -> new GitHubEMUUserHandler(configuration, client, this, schema));
buildSchema(schemaBuilder, GitHubEMUGroupHandler.createSchema(configuration, client).build(),
(schema) -> new GitHubEMUGroupHandler(configuration, client, this, schema));
+ buildSchema(schemaBuilder, GitHubCopilotSeatHandler.createSchema(configuration, client).build(),
+ (schema) -> new GitHubCopilotSeatHandler(configuration, client, this, schema));
// Define operation options
schemaBuilder.defineOperationOption(OperationOptionInfoBuilder.buildAttributesToGet(), SearchOp.class);
diff --git a/src/main/java/jp/openstandia/connector/github/GitHubEMUUserHandler.java b/src/main/java/jp/openstandia/connector/github/GitHubEMUUserHandler.java
index 2258eba..38f7511 100644
--- a/src/main/java/jp/openstandia/connector/github/GitHubEMUUserHandler.java
+++ b/src/main/java/jp/openstandia/connector/github/GitHubEMUUserHandler.java
@@ -21,6 +21,7 @@
import org.kohsuke.github.*;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.Set;
import java.util.stream.Stream;
@@ -55,7 +56,7 @@ public static SchemaDefinition.Builder createSchema(AbstractGitHubConfiguration
sb.addUid("userId",
SchemaDefinition.Types.UUID,
null,
- (source) -> source.id,
+ source -> source.id,
"id",
NOT_CREATABLE, NOT_UPDATEABLE
);
@@ -66,7 +67,7 @@ public static SchemaDefinition.Builder createSchema(AbstractGitHubConfiguration
SchemaDefinition.Types.STRING_CASE_IGNORE,
(source, dest) -> dest.userName = source,
(source, dest) -> dest.replace("userName", source),
- (source) -> source.userName,
+ source -> source.userName,
null,
REQUIRED
);
@@ -76,28 +77,24 @@ public static SchemaDefinition.Builder createSchema(AbstractGitHubConfiguration
SchemaDefinition.Types.BOOLEAN,
(source, dest) -> dest.active = source,
(source, dest) -> dest.replace("active", source),
- (source) -> source.active,
+ source -> source.active,
"active"
);
// Attributes
sb.add("externalId",
SchemaDefinition.Types.STRING,
- (source, dest) -> {
- dest.externalId = source;
- },
+ (source, dest) -> dest.externalId = source,
(source, dest) -> dest.replace("externalId", source),
- (source) -> source.externalId,
+ source -> source.externalId,
null,
REQUIRED
);
sb.add("displayName",
SchemaDefinition.Types.STRING,
- (source, dest) -> {
- dest.displayName = source;
- },
+ (source, dest) -> dest.displayName = source,
(source, dest) -> dest.replace("displayName", source),
- (source) -> source.displayName,
+ source -> source.displayName,
null
);
sb.add("name.formatted",
@@ -109,7 +106,7 @@ public static SchemaDefinition.Builder createSchema(AbstractGitHubConfiguration
dest.name.formatted = source;
},
(source, dest) -> dest.replace("name.formatted", source),
- (source) -> source.name != null ? source.name.formatted : null,
+ source -> source.name != null ? source.name.formatted : null,
null
);
sb.add("name.givenName",
@@ -121,7 +118,7 @@ public static SchemaDefinition.Builder createSchema(AbstractGitHubConfiguration
dest.name.givenName = source;
},
(source, dest) -> dest.replace("name.givenName", source),
- (source) -> source.name != null ? source.name.givenName : null,
+ source -> source.name != null ? source.name.givenName : null,
null
);
sb.add("name.familyName",
@@ -133,7 +130,7 @@ public static SchemaDefinition.Builder createSchema(AbstractGitHubConfiguration
dest.name.familyName = source;
},
(source, dest) -> dest.replace("name.familyName", source),
- (source) -> source.name != null ? source.name.familyName : null,
+ source -> source.name != null ? source.name.familyName : null,
null
);
// SCIM schema has "emails", but we define "primaryEmail" as single value here for easy mapping in IDM
@@ -161,7 +158,7 @@ public static SchemaDefinition.Builder createSchema(AbstractGitHubConfiguration
newEmail.primary = true;
dest.replace(newEmail);
},
- (source) -> source.emails != null && !source.emails.isEmpty() ? source.emails.get(0).value : null,
+ source -> source.emails != null && !source.emails.isEmpty() ? source.emails.get(0).value : null,
null,
REQUIRED
);
@@ -190,7 +187,7 @@ public static SchemaDefinition.Builder createSchema(AbstractGitHubConfiguration
newRole.primary = true;
dest.replace(newRole);
},
- (source) -> source.roles != null && !source.roles.isEmpty() ? source.roles.get(0).value : null,
+ source -> source.roles != null && !source.roles.isEmpty() ? source.roles.get(0).value : null,
null
);
@@ -201,7 +198,7 @@ public static SchemaDefinition.Builder createSchema(AbstractGitHubConfiguration
null,
null,
null,
- (source) -> source.groups != null ? source.groups.stream().filter(x -> x.ref.contains("/Groups/")).map(x -> x.value) : Stream.empty(),
+ source -> source.groups != null ? source.groups.stream().filter(x -> x.ref.contains("/Groups/")).map(x -> x.value) : Stream.empty(),
null,
NOT_CREATABLE, NOT_UPDATEABLE, NOT_RETURNED_BY_DEFAULT
);
@@ -210,14 +207,14 @@ public static SchemaDefinition.Builder createSchema(AbstractGitHubConfiguration
sb.add("meta.created",
SchemaDefinition.Types.DATETIME,
null,
- (source) -> source.meta != null ? toZoneDateTimeForISO8601OffsetDateTime(source.meta.created) : null,
+ source -> source.meta != null ? toZoneDateTimeForISO8601OffsetDateTime(source.meta.created) : null,
null,
NOT_CREATABLE, NOT_UPDATEABLE
);
sb.add("meta.lastModified",
SchemaDefinition.Types.DATETIME,
null,
- (source) -> source.meta != null ? toZoneDateTimeForISO8601OffsetDateTime(source.meta.lastModified) : null,
+ source -> source.meta != null ? toZoneDateTimeForISO8601OffsetDateTime(source.meta.lastModified) : null,
null,
NOT_CREATABLE, NOT_UPDATEABLE
);
@@ -232,9 +229,7 @@ public Uid create(Set attributes) {
SCIMEMUUser user = new SCIMEMUUser();
SCIMEMUUser mapped = schemaDefinition.apply(attributes, user);
- Uid created = client.createEMUUser(mapped);
-
- return created;
+ return client.createEMUUser(mapped);
}
@Override
@@ -247,7 +242,7 @@ public Set updateDelta(Uid uid, Set modification
client.patchEMUUser(uid, dest);
}
- return null;
+ return Collections.emptySet();
}
@Override
@@ -285,7 +280,7 @@ public int getByName(Name name, ResultsHandler resultsHandler, OperationOptions
public int getAll(ResultsHandler resultsHandler, OperationOptions options,
Set returnAttributesSet, Set fetchFieldsSet,
boolean allowPartialAttributeValues, int pageSize, int pageOffset) {
- return client.getEMUUsers((u) -> resultsHandler.handle(toConnectorObject(schemaDefinition, u, returnAttributesSet, allowPartialAttributeValues)),
+ return client.getEMUUsers(u -> resultsHandler.handle(toConnectorObject(schemaDefinition, u, returnAttributesSet, allowPartialAttributeValues)),
options, fetchFieldsSet, pageSize, pageOffset);
}
}
diff --git a/src/main/java/jp/openstandia/connector/github/GitHubSchema.java b/src/main/java/jp/openstandia/connector/github/GitHubSchema.java
deleted file mode 100644
index 1805bd7..0000000
--- a/src/main/java/jp/openstandia/connector/github/GitHubSchema.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * 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.github;
-
-import org.identityconnectors.framework.common.objects.*;
-import org.identityconnectors.framework.spi.operations.SearchOp;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Schema for GitHub objects.
- *
- * @author Hiroyuki Wada
- */
-public class GitHubSchema extends AbstractGitHubSchema {
-
- public final Schema schema;
- public final Map userSchema;
- public final Map roleSchema;
-
- public GitHubSchema(GitHubConfiguration configuration, GitHubClient client) {
- super(configuration, client);
-
- ObjectClassInfo userSchemaInfo = GitHubUserHandler.getUserSchema();
- ObjectClassInfo roleSchemaInfo = GitHubTeamHandler.getRoleSchema();
-
- SchemaBuilder schemaBuilder = new SchemaBuilder(GitHubConnector.class);
-
- buildSchema(schemaBuilder, userSchemaInfo,
- (objectClassInfo) -> new GitHubUserHandler(configuration, client, this));
- buildSchema(schemaBuilder, roleSchemaInfo,
- (objectClassInfo) -> new GitHubTeamHandler(configuration, client, this));
-
- // Define operation options
- schemaBuilder.defineOperationOption(OperationOptionInfoBuilder.buildAttributesToGet(), SearchOp.class);
- schemaBuilder.defineOperationOption(OperationOptionInfoBuilder.buildReturnDefaultAttributes(), SearchOp.class);
-
- this.schema = schemaBuilder.build();
-
- Map userSchemaMap = new HashMap<>();
- for (AttributeInfo info : userSchemaInfo.getAttributeInfo()) {
- userSchemaMap.put(info.getName(), info);
- }
-
- Map roleSchemaMp = new HashMap<>();
- for (AttributeInfo info : roleSchemaInfo.getAttributeInfo()) {
- roleSchemaMp.put(info.getName(), info);
- }
-
- this.userSchema = Collections.unmodifiableMap(userSchemaMap);
- this.roleSchema = Collections.unmodifiableMap(roleSchemaMp);
- }
-
- @Override
- public Schema getSchema() {
- return schema;
- }
-}
diff --git a/src/main/java/jp/openstandia/connector/github/GitHubTeamHandler.java b/src/main/java/jp/openstandia/connector/github/GitHubTeamHandler.java
deleted file mode 100644
index a639416..0000000
--- a/src/main/java/jp/openstandia/connector/github/GitHubTeamHandler.java
+++ /dev/null
@@ -1,229 +0,0 @@
-/*
- * 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.github;
-
-import org.identityconnectors.common.logging.Log;
-import org.identityconnectors.framework.common.exceptions.InvalidAttributeValueException;
-import org.identityconnectors.framework.common.objects.*;
-
-import java.util.HashSet;
-import java.util.Set;
-
-import static jp.openstandia.connector.github.GitHubUtils.*;
-
-/**
- * Handle GitHub Team object.
- *
- * @author Hiroyuki Wada
- */
-public class GitHubTeamHandler extends AbstractGitHubHandler {
-
- public static final ObjectClass TEAM_OBJECT_CLASS = new ObjectClass("team");
-
- private static final Log LOGGER = Log.getLog(GitHubTeamHandler.class);
-
- // Unique and unchangeable.
- // Don't use "id" here because it conflicts midpoint side.
- // The format is :.
- private static final String ATTR_TEAM_DATABASE_ID_WITH_NODE_ID = "teamId";
-
- // Unique and changeable.
- private static final String ATTR_TEAM_NAME = "name";
-
- // Attributes
- public static final String ATTR_DESCRIPTION = "description";
- public static final String ATTR_PRIVACY = "privacy"; // secret, visible(closed in REST API)
-
- // Readonly
- // Unique and unchangeable (generated).
- public static final String ATTR_TEAM_DATABASE_ID = "databaseId";
- // Unique and changeable (generated from name).
- public static final String ATTR_SLUG = "slug";
- // Unique and unchangeable (generated).
- public static final String ATTR_TEAM_NODE_ID = "nodeId";
-
- // Association
- public static final String ATTR_PARENT_TEAM_ID = "parentTeamId";
-
- public GitHubTeamHandler(GitHubConfiguration configuration, GitHubClient client,
- GitHubSchema schema) {
- super(configuration, client, schema);
- }
-
- public static ObjectClassInfo getRoleSchema() {
- ObjectClassInfoBuilder builder = new ObjectClassInfoBuilder();
- builder.setType(TEAM_OBJECT_CLASS.getObjectClassValue());
-
- // id (__UID__)
- builder.addAttributeInfo(
- AttributeInfoBuilder.define(Uid.NAME)
- .setRequired(false) // Must be optional. It is not present for create operations
- .setCreateable(false)
- .setUpdateable(false)
- .setNativeName(ATTR_TEAM_DATABASE_ID_WITH_NODE_ID)
- .build());
-
- // name (__NAME__)
- builder.addAttributeInfo(
- AttributeInfoBuilder.define(Name.NAME)
- .setRequired(true)
- .setNativeName(ATTR_TEAM_NAME)
- .setSubtype(AttributeInfo.Subtypes.STRING_CASE_IGNORE)
- .build());
-
- // Attributes
- builder.addAttributeInfo(
- AttributeInfoBuilder.define(ATTR_DESCRIPTION)
- .setRequired(false)
- .build());
- builder.addAttributeInfo(
- AttributeInfoBuilder.define(ATTR_PRIVACY)
- .setRequired(false)
- .build());
-
- // Readonly
- builder.addAttributeInfo(
- AttributeInfoBuilder.define(ATTR_TEAM_DATABASE_ID)
- .setRequired(false)
- .setCreateable(false)
- .setUpdateable(false)
- .setType(Long.class)
- .build());
- builder.addAttributeInfo(
- AttributeInfoBuilder.define(ATTR_SLUG)
- .setRequired(false)
- .setCreateable(false)
- .setUpdateable(false)
- .build());
- builder.addAttributeInfo(
- AttributeInfoBuilder.define(ATTR_TEAM_NODE_ID)
- .setRequired(false)
- .setCreateable(false)
- .setUpdateable(false)
- .build());
-
- // Association
- builder.addAttributeInfo(
- AttributeInfoBuilder.define(ATTR_PARENT_TEAM_ID)
- .setRequired(false)
- // Association value is expected as string value in midPoint
- // https://github.com/Evolveum/midpoint/blob/50f01966cfa6c2df458f218c255cc2e0d0631b39/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/shadowmanager/ShadowManager.java#L554
- //.setType(Long.class)
- .build());
-
- ObjectClassInfo schemaInfo = builder.build();
-
- LOGGER.ok("The constructed GitHub Team schema: {0}", schemaInfo);
-
- return schemaInfo;
- }
-
- @Override
- public Uid create(Set attributes) {
- String name = null;
- String description = null;
- String privacy = null;
- Long parentTeamDatabaseId = null;
-
- for (Attribute attr : attributes) {
- if (attr.is(Name.NAME)) {
- name = AttributeUtil.getStringValue(attr);
-
- } else if (attr.is(ATTR_DESCRIPTION)) {
- description = AttributeUtil.getStringValue(attr);
-
- } else if (attr.is(ATTR_PRIVACY)) {
- privacy = AttributeUtil.getStringValue(attr);
-
- } else if (attr.is(ATTR_PARENT_TEAM_ID)) {
- String s = AttributeUtil.getStringValue(attr);
- parentTeamDatabaseId = getTeamDatabaseId(s);
- }
- }
-
- if (name == null) {
- throw new InvalidAttributeValueException("GitHub Team name is required");
- }
-
- return client.createTeam(schema, name, description, privacy, parentTeamDatabaseId);
- }
-
- @Override
- public Set updateDelta(Uid uid, Set modifications, OperationOptions options) {
- String name = null;
- String description = null;
- String privacy = null;
- Long parentTeamId = null;
- boolean clearParent = false;
-
- for (AttributeDelta attr : modifications) {
- if (attr.is(Name.NAME)) {
- name = AttributeDeltaUtil.getStringValue(attr);
-
- } else if (attr.is(ATTR_DESCRIPTION)) {
- description = toResourceAttributeValue(AttributeDeltaUtil.getStringValue(attr));
-
- } else if (attr.is(ATTR_PRIVACY)) {
- privacy = AttributeDeltaUtil.getStringValue(attr);
-
- } else if (attr.is(ATTR_PARENT_TEAM_ID)) {
- String s = AttributeDeltaUtil.getStringValue(attr);
- if (s != null) {
- parentTeamId = getTeamDatabaseId(s);
- } else {
- clearParent = true;
- }
- }
- }
-
- Uid updated = client.updateTeam(schema, uid, name, description, privacy, parentTeamId, clearParent, options);
-
- // Detected changed NAME(slug)
- if (!uid.getNameHintValue().equals(updated.getNameHintValue())) {
- AttributeDelta newName = AttributeDeltaBuilder.build(Name.NAME, updated.getNameHintValue());
- Set sideEffects = new HashSet<>();
- sideEffects.add(newName);
-
- return sideEffects;
- }
-
- return null;
- }
-
- public void delete(Uid uid, OperationOptions options) {
- client.deleteTeam(schema, uid, options);
- }
-
- @Override
- public void query(GitHubFilter filter, ResultsHandler resultsHandler, OperationOptions options) {
- // Create full attributesToGet by RETURN_DEFAULT_ATTRIBUTES + ATTRIBUTES_TO_GET
- Set attributesToGet = createFullAttributesToGet(schema.roleSchema, options);
- boolean allowPartialAttributeValues = shouldAllowPartialAttributeValues(options);
-
- if (filter == null) {
- client.getTeams(schema,
- resultsHandler, options, attributesToGet, allowPartialAttributeValues, configuration.getQueryPageSize());
- } else {
- if (filter.isByUid()) {
- client.getTeam(schema, filter.uid,
- resultsHandler, options, attributesToGet, allowPartialAttributeValues, configuration.getQueryPageSize());
- } else {
- client.getTeam(schema, filter.name,
- resultsHandler, options, attributesToGet, allowPartialAttributeValues, configuration.getQueryPageSize());
- }
- }
- }
-}
diff --git a/src/main/java/jp/openstandia/connector/github/GitHubUserHandler.java b/src/main/java/jp/openstandia/connector/github/GitHubUserHandler.java
deleted file mode 100644
index 53db467..0000000
--- a/src/main/java/jp/openstandia/connector/github/GitHubUserHandler.java
+++ /dev/null
@@ -1,341 +0,0 @@
-/*
- * 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.github;
-
-import org.identityconnectors.common.logging.Log;
-import org.identityconnectors.framework.common.objects.*;
-import org.kohsuke.github.SCIMEmail;
-import org.kohsuke.github.SCIMName;
-import org.kohsuke.github.SCIMUser;
-
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-import static jp.openstandia.connector.github.GitHubUtils.*;
-
-/**
- * Handle GitHub user object.
- *
- * @author Hiroyuki Wada
- */
-public class GitHubUserHandler extends AbstractGitHubHandler {
-
- public static final ObjectClass USER_OBJECT_CLASS = new ObjectClass("user");
-
- private static final Log LOGGER = Log.getLog(GitHubUserHandler.class);
-
- // Unique and unchangeable. This is SCIM user id.
- // Don't use "id" here because it conflicts midpoint side.
- private static final String ATTR_USER_ID = "scimUserId";
-
- // Unique and changeable. This is GitHub login(username) and scimUserName(login:scimUserName).
- public static final String ATTR_USER_NAME = "userName";
-
- // Attributes
- public static final String ATTR_SCIM_USER_NAME = "scimUserName";
- public static final String ATTR_SCIM_EMAIL = "scimEmail";
- public static final String ATTR_SCIM_GIVEN_NAME = "scimGivenName";
- public static final String ATTR_SCIM_FAMILY_NAME = "scimFamilyName";
- public static final String ATTR_SCIM_EXTERNAL_ID = "scimExternalId";
- public static final String ATTR_ORGANIZATION_ROLE = "organizationRole";
-
- // Readonly
- // Only fetched by GraphQL ExternalIdentity through all users query due to GitHub API limitation.
- public static final String ATTR_USER_LOGIN = "login";
-
- // Association
- public static final String ATTR_TEAMS = "teams"; // List of teamId(databaseId:nodeId)
- public static final String ATTR_MAINTAINER_TEAMS = "maintainerTeams"; // List of teamId(databaseId:nodeId)
-
- public GitHubUserHandler(GitHubConfiguration configuration, GitHubClient client,
- GitHubSchema schema) {
- super(configuration, client, schema);
- }
-
- public static ObjectClassInfo getUserSchema() {
- ObjectClassInfoBuilder builder = new ObjectClassInfoBuilder();
- builder.setType(USER_OBJECT_CLASS.getObjectClassValue());
-
- // scimUserId (__UID__)
- builder.addAttributeInfo(
- AttributeInfoBuilder.define(Uid.NAME)
- .setRequired(false) // Must be optional. It is not present for create operations
- .setCreateable(false)
- .setUpdateable(false)
- .setSubtype(AttributeInfo.Subtypes.STRING_CASE_IGNORE)
- .setNativeName(ATTR_USER_ID)
- .build());
-
- // userName (__NAME__)
- builder.addAttributeInfo(
- AttributeInfoBuilder.define(Name.NAME)
- .setRequired(true)
- .setSubtype(AttributeInfo.Subtypes.STRING_CASE_IGNORE)
- .setNativeName(ATTR_USER_NAME)
- .build());
-
- // attributes
- builder.addAttributeInfo(
- AttributeInfoBuilder.define(ATTR_SCIM_EMAIL)
- .setRequired(true)
- .setSubtype(AttributeInfo.Subtypes.STRING_CASE_IGNORE)
- .build());
- builder.addAttributeInfo(
- AttributeInfoBuilder.define(ATTR_SCIM_GIVEN_NAME)
- .setRequired(true)
- .build());
- builder.addAttributeInfo(
- AttributeInfoBuilder.define(ATTR_SCIM_FAMILY_NAME)
- .setRequired(true)
- .build());
- builder.addAttributeInfo(
- AttributeInfoBuilder.define(ATTR_SCIM_EXTERNAL_ID)
- .setRequired(false)
- .build());
-
- // Readonly
- builder.addAttributeInfo(
- AttributeInfoBuilder.define(ATTR_USER_LOGIN)
- .setRequired(false)
- .setCreateable(false)
- .setUpdateable(false)
- .setSubtype(AttributeInfo.Subtypes.STRING_CASE_IGNORE)
- .build());
- builder.addAttributeInfo(
- AttributeInfoBuilder.define(ATTR_SCIM_USER_NAME)
- .setRequired(false)
- .setCreateable(false)
- .setUpdateable(false)
- .setSubtype(AttributeInfo.Subtypes.STRING_CASE_IGNORE)
- .build());
-
- // Association
- builder.addAttributeInfo(
- AttributeInfoBuilder.define(ATTR_TEAMS)
- .setRequired(false)
- .setMultiValued(true)
- // We define the team's UID as string with : format
- // .setType(Integer.class)
- .setReturnedByDefault(false)
- .build());
- builder.addAttributeInfo(
- AttributeInfoBuilder.define(ATTR_MAINTAINER_TEAMS)
- .setRequired(false)
- .setMultiValued(true)
- // We define the team's UID as string with : format
- // .setType(Integer.class)
- .setReturnedByDefault(false)
- .build());
- // TODO: Implement Organization Role schema?
- builder.addAttributeInfo(
- AttributeInfoBuilder.define(ATTR_ORGANIZATION_ROLE)
- .setRequired(false)
- .setReturnedByDefault(false)
- .build());
-
- ObjectClassInfo userSchemaInfo = builder.build();
-
- LOGGER.ok("The constructed GitHub user schema: {0}", userSchemaInfo);
-
- return userSchemaInfo;
- }
-
- @Override
- public Uid create(Set attributes) {
- SCIMUser newUser = new SCIMUser();
- newUser.name = new SCIMName();
-
- for (Attribute attr : attributes) {
- if (attr.is(Name.NAME)) {
- String loginWithScimUserName = AttributeUtil.getStringValue(attr);
- // Throw InvalidAttributeValueException if invalid format
- newUser.userName = getUserSCIMUserName(loginWithScimUserName);
-
- } else if (attr.is(ATTR_SCIM_EMAIL)) {
- SCIMEmail scimEmail = new SCIMEmail();
- scimEmail.value = AttributeUtil.getStringValue(attr);
- newUser.emails = new SCIMEmail[]{scimEmail};
-
- } else if (attr.is(ATTR_SCIM_GIVEN_NAME)) {
- newUser.name.givenName = AttributeUtil.getStringValue(attr);
-
- } else if (attr.is(ATTR_SCIM_FAMILY_NAME)) {
- newUser.name.familyName = AttributeUtil.getStringValue(attr);
-
- } else if (attr.is(ATTR_SCIM_EXTERNAL_ID)) {
- newUser.externalId = AttributeUtil.getStringValue(attr);
- }
- }
-
- Uid created = client.createUser(schema, newUser);
-
- // Association can't be constructed here because GitHub login is unknown yet.
-
- return created;
- }
-
- @Override
- public Set updateDelta(Uid uid, Set modifications, OperationOptions options) {
- String login = null;
- String scimUserName = null;
- String scimEmail = null;
- String scimGivenName = null;
- String scimFamilyName = null;
- String organizationRole = null;
- Set addTeams = new HashSet<>();
- Set removeTeams = new HashSet<>();
- Set addMaintainerTeams = new HashSet<>();
- Set removeMaintainerTeams = new HashSet<>();
-
- for (AttributeDelta attr : modifications) {
- if (attr.is(Name.NAME)) {
- // Detected modifying userName (e.g. completed the invitation by full reconciliation, update scimUserName)
- String newLoginWithScimUserName = AttributeDeltaUtil.getStringValue(attr);
-
- // Detect scimUserName change
- String newScimUserName = getUserSCIMUserName(newLoginWithScimUserName);
- String oldScimUserName = getUserSCIMUserName(uid);
- if (!newScimUserName.equals(oldScimUserName)) {
- scimUserName = newScimUserName;
- }
-
- // Detect user login change
- String newLogin = getUserLogin(newLoginWithScimUserName);
- String oldLogin = getUserLogin(uid);
- if (!newLogin.equals(oldLogin)) {
- login = newLogin;
- }
-
- } else if (attr.is(ATTR_SCIM_EMAIL)) {
- scimEmail = AttributeDeltaUtil.getStringValue(attr);
-
- } else if (attr.is(ATTR_SCIM_GIVEN_NAME)) {
- scimGivenName = AttributeDeltaUtil.getStringValue(attr);
-
- } else if (attr.is(ATTR_SCIM_FAMILY_NAME)) {
- scimFamilyName = AttributeDeltaUtil.getStringValue(attr);
-
- } else if (attr.is(ATTR_ORGANIZATION_ROLE)) {
- organizationRole = toResourceAttributeValue(AttributeDeltaUtil.getStringValue(attr), "member");
-
- } else if (attr.is(ATTR_TEAMS)) {
- if (attr.getValuesToAdd() != null) {
- addTeams = attr.getValuesToAdd().stream().map(v -> v.toString()).collect(Collectors.toSet());
- }
- if (attr.getValuesToRemove() != null) {
- removeTeams = attr.getValuesToRemove().stream().map(v -> v.toString()).collect(Collectors.toSet());
- }
-
- } else if (attr.is(ATTR_MAINTAINER_TEAMS)) {
- if (attr.getValuesToAdd() != null) {
- addMaintainerTeams = attr.getValuesToAdd().stream().map(v -> v.toString()).collect(Collectors.toSet());
- }
- if (attr.getValuesToRemove() != null) {
- removeMaintainerTeams = attr.getValuesToRemove().stream().map(v -> v.toString()).collect(Collectors.toSet());
- }
- }
- }
-
- String newNameValue = client.updateUser(schema, uid, scimUserName, scimEmail, scimGivenName, scimFamilyName, login, options);
-
- String userLogin = resolveUserLogin(uid, newNameValue);
-
- // Organization role and Association
- if (userLogin != null &&
- (organizationRole != null ||
- !addTeams.isEmpty() || !removeTeams.isEmpty() ||
- !addMaintainerTeams.isEmpty() || !removeMaintainerTeams.isEmpty()
- )) {
-
- // do update organization role
- if (organizationRole != null) {
- // If the user login is stale, it throws UnknownUidException.
- // IDM handle the exception then do discovery process if needed.
- client.assignOrganizationRole(userLogin, organizationRole);
- }
-
- // assign/unassign the teams
- TeamAssignmentResolver resolver = new TeamAssignmentResolver(addTeams, removeTeams, addMaintainerTeams, removeMaintainerTeams);
-
- // If the user login is stale, it throws UnknownUidException.
- // IDM handle the exception then do discovery process if needed.
- client.unassignTeams(userLogin, resolver.resolvedRemoveTeams);
- client.assignTeams(userLogin, "member", resolver.resolvedAddTeams);
- client.assignTeams(userLogin, "maintainer", resolver.resolvedAddMaitainerTeams);
- }
-
- // Detect NAME changing
- if (newNameValue != null) {
- Set sideEffects = new HashSet<>();
- AttributeDelta newName = AttributeDeltaBuilder.build(Name.NAME, newNameValue);
- sideEffects.add(newName);
-
- return sideEffects;
- }
-
- return null;
- }
-
- private String resolveUserLogin(Uid oldUid, String newNameValue) {
- if (newNameValue != null) {
- return getUserLogin(newNameValue);
- }
-
- String userLogin = getUserLogin(oldUid);
- if (!userLogin.equals(UNKNOWN_USER_NAME)) {
- return userLogin;
- }
- // Can't resolve yet due to not completed invitation
- return null;
- }
-
- @Override
- public void delete(Uid uid, OperationOptions options) {
- String userLogin = getUserLogin(uid);
- if (!userLogin.equals(UNKNOWN_USER_NAME)) {
- // Fix https://github.com/openstandia/connector-github/issues/6
- // GitHub maintains the user's team association after deletion
- // So, we need to remove the association first
- List teamIds = client.getTeamIdsByUsername(userLogin, configuration.getQueryPageSize());
- client.unassignTeams(userLogin, teamIds);
- }
-
- // Finally, do delete the user
- client.deleteUser(schema, uid, options);
- }
-
- @Override
- public void query(GitHubFilter filter, ResultsHandler resultsHandler, OperationOptions options) {
- // Create full attributesToGet by RETURN_DEFAULT_ATTRIBUTES + ATTRIBUTES_TO_GET
- Set attributesToGet = createFullAttributesToGet(schema.userSchema, options);
- boolean allowPartialAttributeValues = shouldAllowPartialAttributeValues(options);
-
- if (filter == null) {
- client.getUsers(schema,
- resultsHandler, options, attributesToGet, allowPartialAttributeValues, configuration.getQueryPageSize());
- } else {
- if (filter.isByUid()) {
- client.getUser(schema, filter.uid,
- resultsHandler, options, attributesToGet, allowPartialAttributeValues, configuration.getQueryPageSize());
- } else {
- client.getUser(schema, filter.name,
- resultsHandler, options, attributesToGet, allowPartialAttributeValues, configuration.getQueryPageSize());
- }
- }
- }
-}
diff --git a/src/main/java/jp/openstandia/connector/github/GitHubUtils.java b/src/main/java/jp/openstandia/connector/github/GitHubUtils.java
deleted file mode 100644
index eb2ef9e..0000000
--- a/src/main/java/jp/openstandia/connector/github/GitHubUtils.java
+++ /dev/null
@@ -1,254 +0,0 @@
-/*
- * 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.github;
-
-import org.identityconnectors.framework.common.exceptions.InvalidAttributeValueException;
-import org.identityconnectors.framework.common.objects.AttributeInfo;
-import org.identityconnectors.framework.common.objects.Name;
-import org.identityconnectors.framework.common.objects.OperationOptions;
-import org.identityconnectors.framework.common.objects.Uid;
-import org.kohsuke.github.*;
-
-import java.time.OffsetDateTime;
-import java.time.ZonedDateTime;
-import java.util.*;
-import java.util.stream.Collectors;
-
-/**
- * Provides utility methods.
- *
- * @author Hiroyuki Wada
- */
-public class GitHubUtils {
-
- public static ZonedDateTime toZoneDateTime(OffsetDateTime dateTime) {
- return dateTime.toZonedDateTime();
- }
-
- public static String toResourceAttributeValue(String s) {
- // To support deleting value, return empty string
- if (s == null) {
- return "";
- }
-
- return s;
- }
-
- public static String toResourceAttributeValue(String s, String defaultValue) {
- if (s == null) {
- return defaultValue;
- }
-
- return s;
- }
-
- public static boolean shouldReturn(Set attrsToGetSet, String attr) {
- if (attrsToGetSet == null) {
- return true;
- }
- return attrsToGetSet.contains(attr);
- }
-
- /**
- * Check if ALLOW_PARTIAL_ATTRIBUTE_VALUES == true.
- *
- * @param options operation options
- * @return true: allow partial attribute values, false: not allow
- */
- public static boolean shouldAllowPartialAttributeValues(OperationOptions options) {
- // If the option isn't set from IDM, it may be null.
- return Boolean.TRUE.equals(options.getAllowPartialAttributeValues());
- }
-
- /**
- * Check if RETURN_DEFAULT_ATTRIBUTES == true.
- *
- * @param options operation options
- * @return true: return default attributes, false: not return
- */
- public static boolean shouldReturnDefaultAttributes(OperationOptions options) {
- // If the option isn't set from IDM, it may be null.
- return Boolean.TRUE.equals(options.getReturnDefaultAttributes());
- }
-
- /**
- * Create full set of ATTRIBUTES_TO_GET which is composed by RETURN_DEFAULT_ATTRIBUTES + ATTRIBUTES_TO_GET.
- *
- * @param schema schema map
- * @param options operation options
- * @return set of the attributes to get
- */
- public static Set createFullAttributesToGet(Map schema, OperationOptions options) {
- Set attributesToGet = null;
- if (shouldReturnDefaultAttributes(options)) {
- attributesToGet = new HashSet<>();
- attributesToGet.addAll(toReturnedByDefaultAttributesSet(schema));
- }
- if (options.getAttributesToGet() != null) {
- if (attributesToGet == null) {
- attributesToGet = new HashSet<>();
- }
- for (String a : options.getAttributesToGet()) {
- attributesToGet.add(a);
- }
- }
- return attributesToGet;
- }
-
- private static Set toReturnedByDefaultAttributesSet(Map schema) {
- return schema.entrySet().stream()
- .filter(entry -> entry.getValue().isReturnedByDefault())
- .map(entry -> entry.getKey())
- .collect(Collectors.toSet());
- }
-
- public static Throwable getRootCause(final Throwable t) {
- final List list = getThrowableList(t);
- return list.size() < 2 ? null : list.get(list.size() - 1);
- }
-
- private static List getThrowableList(Throwable t) {
- final List list = new ArrayList<>();
- while (t != null && !list.contains(t)) {
- list.add(t);
- t = t.getCause();
- }
- return list;
- }
-
- public static Uid toUserUid(SCIMUser user) {
- return new Uid(user.id, new Name(toUserName(user)));
- }
-
- public static String toUserName(SCIMUser user) {
- return toUserName(null, user.userName);
- }
-
- public static String toUserName(String login, String scimUserName) {
- if (login == null) {
- // Need to return the format with :
- // GitHub username policy is:
- // Username may only contain alphanumeric characters or single hyphens, and cannot begin or end with a hyphen.
- // So, return special "_unknown_" tag here because we can't determine the user login name yet
- return UNKNOWN_USER_NAME + ":" + scimUserName;
- }
- return login + ":" + scimUserName;
- }
-
- public static final String UNKNOWN_USER_NAME = "_unknown_";
-
- public static String getUserLogin(Uid uid) throws InvalidAttributeValueException {
- return getUserLogin(uid.getNameHintValue());
- }
-
- public static String getUserSCIMUserName(Uid uid) throws InvalidAttributeValueException {
- return getUserSCIMUserName(uid.getNameHintValue());
- }
-
- public static String getUserSCIMUserName(Name name) throws InvalidAttributeValueException {
- return getUserSCIMUserName(name.getNameValue());
- }
-
- public static String getUserSCIMUserName(String nameValue) throws InvalidAttributeValueException {
- return parseUserNameValue(nameValue)[1];
- }
-
- public static String getUserLogin(Name name) throws InvalidAttributeValueException {
- return getUserLogin(name.getNameValue());
- }
-
- public static String getUserLogin(String nameValue) throws InvalidAttributeValueException {
- return parseUserNameValue(nameValue)[0];
- }
-
- private static String[] parseUserNameValue(String nameValue) throws InvalidAttributeValueException {
- String[] split = nameValue.split(":");
- if (split.length != 2) {
- throw new InvalidAttributeValueException("GitHub userName must be \"login:scimUserName\" format. value: " + nameValue);
- }
- return split;
- }
-
- public static String toTeamUid(GHTeam team) {
- return toTeamUid(String.valueOf(team.getId()), team.getNodeId());
- }
-
- public static String toTeamUid(GraphQLTeamEdge teamEdge) {
- return toTeamUid(teamEdge.node);
- }
-
- public static String toTeamUid(GraphQLTeam team) {
- return toTeamUid(team.databaseId.toString(), team.id);
- }
-
- private static String toTeamUid(String databaseId, String nodeId) {
- return databaseId + ":" + nodeId;
- }
-
- public static long getTeamDatabaseId(Uid uid) {
- return getTeamDatabaseId(uid.getUidValue());
- }
-
- public static long getTeamDatabaseId(String uid) throws InvalidAttributeValueException {
- String databaseId = parseTeamUidValue(uid)[0];
-
- try {
- return Long.parseLong(databaseId);
- } catch (NumberFormatException e) {
- throw new InvalidAttributeValueException("Unexpected teamId: " + uid);
- }
- }
-
- public static String getTeamNodeId(Uid uid) throws InvalidAttributeValueException {
- return parseTeamUidValue(uid.getUidValue())[1];
- }
-
- private static String[] parseTeamUidValue(String uidValue) throws InvalidAttributeValueException {
- String[] split = uidValue.split(":");
- if (split.length != 2) {
- throw new InvalidAttributeValueException("GitHub teamId must be \"databaseId:nodeId\" format. value: " + uidValue);
- }
- return split;
- }
-
- public static GHTeam.Privacy toGHTeamPrivacy(String privacy) throws InvalidAttributeValueException {
- try {
- // Validation
- GraphQLTeamPrivacy gp = GraphQLTeamPrivacy.valueOf(privacy.toUpperCase());
-
- // Need to convert
- GHTeam.Privacy ghp = null;
- if (gp == GraphQLTeamPrivacy.SECRET) {
- ghp = GHTeam.Privacy.SECRET;
- } else {
- ghp = GHTeam.Privacy.CLOSED;
- }
- return ghp;
-
- } catch (IllegalArgumentException e) {
- throw new InvalidAttributeValueException("GitHub Team privacy must be \"visible\" or \"secret\": " + privacy);
- }
- }
-
- public static String toGroupId(SCIMEMUGroup group) {
- return group.id;
- }
-
- public static String toGroupName(SCIMEMUGroup group) {
- return group.displayName;
- }
-
-}
diff --git a/src/main/java/jp/openstandia/connector/github/TeamAssignmentResolver.java b/src/main/java/jp/openstandia/connector/github/TeamAssignmentResolver.java
deleted file mode 100644
index b82800d..0000000
--- a/src/main/java/jp/openstandia/connector/github/TeamAssignmentResolver.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * 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.github;
-
-import java.util.Collections;
-import java.util.Set;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-public class TeamAssignmentResolver {
- private final Set origAddTeams;
- private final Set origRemoveTeams;
- private final Set origAddMaintainerTeams;
- private final Set origRemoveMaintainerTeams;
-
- public Set resolvedAddTeams;
- public Set resolvedAddMaitainerTeams;
- public Set resolvedRemoveTeams;
-
- public TeamAssignmentResolver(Set addTeams, Set removeTeams, Set addMaintainerTeams, Set removeMaintainerTeams) {
- this.origAddTeams = Collections.unmodifiableSet(addTeams);
- this.origRemoveTeams = Collections.unmodifiableSet(removeTeams);
- this.origAddMaintainerTeams = Collections.unmodifiableSet(addMaintainerTeams);
- this.origRemoveMaintainerTeams = Collections.unmodifiableSet(removeMaintainerTeams);
-
- resolve();
- }
-
- private void resolve() {
- // If same team is assigned for both teams and maintainer teams, we assign as maintainer.
- Set addTeams = origAddTeams.stream()
- .filter(t -> !origAddMaintainerTeams.contains(t))
- .collect(Collectors.toSet());
-
- this.resolvedAddTeams = Collections.unmodifiableSet(addTeams);
-
- // if same team is unassigned as member and assigned as maintainer, we assign as maintainer.
- Set removeTeams = origRemoveTeams.stream()
- .filter(t -> !origAddMaintainerTeams.contains(t))
- .collect(Collectors.toSet());
-
- // if same team is unassigned as maintainer and assigned as member, we assign as member.
- Set removeMaintainerTeams = origRemoveMaintainerTeams.stream()
- .filter(t -> !addTeams.contains(t))
- .collect(Collectors.toSet());
-
- this.resolvedAddMaitainerTeams = origAddMaintainerTeams;
-
- // If same team is unassigned for both teams and maintainer teams, we only unassign it one time.
- this.resolvedRemoveTeams = Collections.unmodifiableSet(
- Stream.concat(removeTeams.stream(), removeMaintainerTeams.stream()).collect(Collectors.toSet())
- );
- }
-}
\ No newline at end of file
diff --git a/src/main/java/jp/openstandia/connector/github/rest/GitHubEMURESTClient.java b/src/main/java/jp/openstandia/connector/github/rest/GitHubEMURESTClient.java
index eb6e5fa..8b3af7f 100644
--- a/src/main/java/jp/openstandia/connector/github/rest/GitHubEMURESTClient.java
+++ b/src/main/java/jp/openstandia/connector/github/rest/GitHubEMURESTClient.java
@@ -26,7 +26,7 @@
import org.identityconnectors.framework.common.objects.OperationOptions;
import org.identityconnectors.framework.common.objects.Uid;
import org.kohsuke.github.*;
-import org.kohsuke.github.extras.okhttp3.OkHttpConnector;
+import org.kohsuke.github.extras.okhttp3.*;
import java.io.IOException;
import java.util.List;
@@ -48,18 +48,13 @@ public class GitHubEMURESTClient implements GitHubClient {
private String instanceName;
private GitHubExt apiClient;
private long lastAuthenticated;
- private GHEnterpriseExt enterpriseApiClient;
+ GHEnterpriseExt enterpriseApiClient;
public GitHubEMURESTClient(GitHubEMUConfiguration configuration) {
this.configuration = configuration;
-
auth();
}
- public GitHubExt getApiClient() {
- return apiClient;
- }
-
@Override
public void setInstanceName(String instanceName) {
this.instanceName = instanceName;
@@ -286,6 +281,53 @@ public SCIMEMUGroup getEMUGroup(Name name, OperationOptions options, Set
});
}
+ @Override
+ public GitHubCopilotSeat getCopilotSeat(Uid uid, OperationOptions options, Set attributesToGet) {
+ return withAuth(() -> {
+ GitHubCopilotSeat seat = enterpriseApiClient.getCopilotSeatByUid(uid.getUidValue());
+ return seat;
+ });
+ }
+
+ @Override
+ public GitHubCopilotSeat getCopilotSeat(Name name, OperationOptions options, Set attributesToGet) {
+ return withAuth(() -> {
+ GitHubCopilotSeat seat = enterpriseApiClient.getCopilotSeatByDisplayName(name.getNameValue());
+ return seat;
+ });
+ }
+
+ @Override
+ public int getCopilotSeats(QueryHandler handler, OperationOptions options, Set fetchFieldsSet, int pageSize, int pageOffset) {
+ return withAuth(() -> {
+ GitHubCopilotSeatPagedSearchIterable iterable = enterpriseApiClient.listAllSeats(pageSize, pageOffset);
+
+ // 0 means no offset (requested all data)
+ if (pageOffset < 1) {
+ for (GitHubCopilotSeat next : iterable) {
+ if (!handler.handle(next)) {
+ break;
+ }
+ }
+ return iterable.getTotalSeats();
+ }
+
+ // Pagination
+ int count = 0;
+ for (GitHubCopilotSeat next : iterable) {
+ count++;
+ if (!handler.handle(next)) {
+ break;
+ }
+ if (count >= pageSize) {
+ break;
+ }
+ }
+
+ return iterable.getTotalSeats();
+ });
+ }
+
@Override
public int getEMUGroups(QueryHandler handler, OperationOptions options, Set fetchFieldsSet, int pageSize, int pageOffset) {
return withAuth(() -> {
diff --git a/src/main/java/jp/openstandia/connector/github/rest/GitHubRESTClient.java b/src/main/java/jp/openstandia/connector/github/rest/GitHubRESTClient.java
deleted file mode 100644
index 24e0a2b..0000000
--- a/src/main/java/jp/openstandia/connector/github/rest/GitHubRESTClient.java
+++ /dev/null
@@ -1,725 +0,0 @@
-/*
- * 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.github.rest;
-
-import com.spotify.github.v3.clients.PKCS1PEMKey;
-import io.jsonwebtoken.JwtBuilder;
-import io.jsonwebtoken.Jwts;
-import io.jsonwebtoken.SignatureAlgorithm;
-import jp.openstandia.connector.github.*;
-import org.identityconnectors.common.StringUtil;
-import org.identityconnectors.common.logging.Log;
-import org.identityconnectors.framework.common.exceptions.*;
-import org.identityconnectors.framework.common.objects.*;
-import org.kohsuke.github.*;
-import org.kohsuke.github.extras.okhttp3.OkHttpConnector;
-
-import java.io.IOException;
-import java.security.Key;
-import java.security.KeyFactory;
-import java.security.NoSuchAlgorithmException;
-import java.security.PrivateKey;
-import java.security.spec.InvalidKeySpecException;
-import java.security.spec.PKCS8EncodedKeySpec;
-import java.util.*;
-import java.util.concurrent.Callable;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicReference;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-import static jp.openstandia.connector.github.GitHubTeamHandler.*;
-import static jp.openstandia.connector.github.GitHubUserHandler.*;
-import static jp.openstandia.connector.github.GitHubUtils.*;
-
-/**
- * GitHub client implementation which uses Java API for GitHub.
- *
- * @author Hiroyuki Wada
- */
-public class GitHubRESTClient implements GitHubClient {
-
- private static final Log LOGGER = Log.getLog(GitHubRESTClient.class);
-
- private final GitHubConfiguration configuration;
- private String instanceName;
- private GitHubExt apiClient;
- private long lastAuthenticated;
- private GHOrganizationExt orgApiClient;
-
- public GitHubRESTClient(GitHubConfiguration configuration) {
- this.configuration = configuration;
-
- auth();
- }
-
- public GitHubExt getApiClient() {
- return apiClient;
- }
-
- @Override
- public void setInstanceName(String instanceName) {
- this.instanceName = instanceName;
- }
-
- @Override
- public void test() {
- try {
- withAuth(() -> {
- apiClient.checkApiUrlValidity();
- return null;
- });
- } catch (RuntimeException e) {
- throw new ConnectorException("This GitHub connector isn't active.", e);
- }
- }
-
- private static class UnauthorizedException extends ConnectionFailedException {
- public UnauthorizedException(Exception e) {
- super(e);
- }
- }
-
- @Override
- public void auth() {
- AtomicReference privateKey = new AtomicReference<>();
- configuration.getPrivateKey().access((val) -> {
- privateKey.set(String.valueOf(val));
- });
-
- try {
- // First, get app installation token
- GitHub api = new GitHubBuilder()
- .withJwtToken(createJWT(configuration.getAppId(), 60000, privateKey.get()))
- .withConnector(new OkHttpConnector(createClient(configuration)))
- .build();
- GHAppInstallation appInstallation = api.getApp().getInstallationById(configuration.getInstallationId()); // Installation Id
-
- GHAppInstallationToken appInstallationToken = appInstallation.createToken().create();
-
- // Then, get scoped access token by app installation token
-
- GitHubBuilder builder = new GitHubBuilder()
- .withConnector(new OkHttpConnector(createClient(configuration)))
- .withAppInstallationToken(appInstallationToken.getToken());
-
- apiClient = GitHubExt.build(builder);
- lastAuthenticated = System.currentTimeMillis();
-
- orgApiClient = apiClient.getOrganization(configuration.getOrganizationName());
-
- } catch (IOException e) {
- throw new ConnectionFailedException("Failed to authenticate GitHub API", e);
- }
- }
-
- protected ConnectorException handleApiException(Exception e) {
-
- if (e instanceof GHFileNotFoundException) {
- GHFileNotFoundException gfe = (GHFileNotFoundException) e;
- List status = gfe.getResponseHeaderFields().get(null);
-
- if (!status.isEmpty() && status.get(0).contains("400")) {
- return new InvalidAttributeValueException(e);
- }
-
- if (!status.isEmpty() && status.get(0).contains("401")) {
- return new UnauthorizedException(e);
- }
-
- if (!status.isEmpty() && status.get(0).contains("403")) {
- // Including Rate limit error
- return new PermissionDeniedException(e);
- }
-
- if (!status.isEmpty() && status.get(0).contains("404")) {
- return new UnknownUidException(e);
- }
-
- if (!status.isEmpty() && status.get(0).contains("409")) {
- return new AlreadyExistsException(e);
- }
-
- if (!status.isEmpty() && status.get(0).contains("422")) {
- // Create Team API return 422 error if exists
- return new AlreadyExistsException(e);
- }
- }
-
- LOGGER.error(e, "Unexpected exception when calling GitHub API");
-
- return new ConnectorIOException("Failed to call GitHub API", e);
- }
-
- protected T withAuth(Callable callable) {
- // Check the access token expiration
- long now = System.currentTimeMillis();
- if (now > lastAuthenticated + TimeUnit.MINUTES.toMillis(55)) {
- // Refresh the access token
- auth();
- }
-
- try {
- return callable.call();
-
- } catch (Exception e) {
- ConnectorException ce = handleApiException(e);
-
- if (ce instanceof UnauthorizedException) {
- // do re-Auth
- auth();
-
- try {
- // retry
- return callable.call();
-
- } catch (Exception e2) {
- throw handleApiException(e2);
- }
- }
-
- throw ce;
- }
- }
-
- @Override
- public Uid createUser(GitHubSchema schema, SCIMUser newUser) throws AlreadyExistsException {
- return withAuth(() -> {
- SCIMUser created = orgApiClient.createSCIMUser(newUser);
-
- return toUserUid(created);
- });
- }
-
- @Override
- public String updateUser(GitHubSchema schema, Uid uid, String scimUserName, String scimEmail, String scimGivenName,
- String scimFamilyName, String login, OperationOptions options) throws UnknownUidException {
- return withAuth(() -> {
- orgApiClient.updateSCIMUser(uid.getUidValue(), scimUserName, scimEmail, scimGivenName, scimFamilyName);
-
- // Detected NAME is changed
- String oldUserLogin = getUserLogin(uid);
- String oldScimUserName = getUserSCIMUserName(uid);
-
- if ((login != null && !oldUserLogin.equals(login))
- || (scimUserName != null && !oldScimUserName.equals(scimUserName))) {
- String newLogin = login != null ? login : oldUserLogin;
- String newScimUserName = scimUserName != null ? scimUserName : oldScimUserName;
-
- // Return new NAME value
- return toUserName(newLogin, newScimUserName);
- }
-
- return null;
- });
- }
-
- @Override
- public void deleteUser(GitHubSchema schema, Uid uid, OperationOptions options) throws UnknownUidException {
- deleteUser(schema, uid.getUidValue(), options);
- }
-
- private void deleteUser(GitHubSchema schema, String scimUserId, OperationOptions options) throws UnknownUidException {
- withAuth(() -> {
- orgApiClient.deleteSCIMUser(scimUserId);
-
- return null;
- });
- }
-
- @Override
- public void getUsers(GitHubSchema schema, ResultsHandler handler, OperationOptions options, Set attributesToGet,
- boolean allowPartialAttributeValues, int queryPageSize) {
- withAuth(() -> {
- orgApiClient.listExternalIdentities(queryPageSize)
- .forEach(u -> {
- // When we detect a dropped account, we need to delete it then return
- // not found from the organization to re-invite the account.
- if (u.node.isDropped()) {
- try {
- deleteUser(schema, u.node.guid, options);
- } catch (UnknownUidException ignore) {
- LOGGER.warn("Detected unknown Uid when deleting a dropped account");
- }
-
- return;
- }
- handler.handle(toConnectorObject(schema, null, u, attributesToGet, allowPartialAttributeValues, queryPageSize));
- });
- return null;
- });
- }
-
- @Override
- public void getUser(GitHubSchema schema, Uid uid, ResultsHandler handler, OperationOptions options,
- Set attributesToGet, boolean allowPartialAttributeValues, int queryPageSize) {
- withAuth(() -> {
- SCIMUser user = orgApiClient.getSCIMUser(uid.getUidValue());
-
- // SCIM User doesn't contain database ID
- // We need to use NAME value in query Uid as user login.
- // It means IDM can't detect when the user login is changed in GitHub side.
- // To detect the situation, IDM need to do full reconciliation which calls getUsers method.
- String queryLogin = getUserLogin(uid);
-
- handler.handle(toConnectorObject(schema, queryLogin, user, attributesToGet, allowPartialAttributeValues, queryPageSize));
-
- return null;
- });
- }
-
- @Override
- public void getUser(GitHubSchema schema, Name name, ResultsHandler handler, OperationOptions options,
- Set attributesToGet, boolean allowPartialAttributeValues, int queryPageSize) {
- withAuth(() -> {
- String scimUserName = getUserSCIMUserName(name);
-
- SCIMUser user = orgApiClient.getSCIMUserByUserName(scimUserName);
-
- // SCIM User doesn't contain database ID
- // We need to use NAME value in query Uid as user login.
- // It means IDM can't detect when the user login is changed in GitHub side.
- // To detect the situation, IDM need to do full reconciliation which calls getUsers method.
- String queryLogin = getUserLogin(name);
-
- handler.handle(toConnectorObject(schema, queryLogin, user, attributesToGet, allowPartialAttributeValues, queryPageSize));
-
- return null;
- });
- }
-
- @Override
- public List getTeamIdsByUsername(String userLogin, int pageSize) {
- return withAuth(() -> {
- return orgApiClient.listTeams(userLogin, pageSize)
- .toList().stream()
- .filter(t -> t.node.members.totalCount == 1)
- .map(GitHubUtils::toTeamUid)
- .collect(Collectors.toList());
- });
- }
-
- private ConnectorObject toConnectorObject(GitHubSchema schema, String queryLogin, SCIMUser user,
- Set attributesToGet, boolean allowPartialAttributeValues, int queryPageSize) {
-
- final String scimEmail = (user.emails != null && user.emails.length > 0) ? user.emails[0].value : null;
-
- String scimGivenName = user.name != null ? user.name.givenName : null;
- String scimFamilyName = user.name != null ? user.name.familyName : null;
-
- return toConnectorObject(schema, queryLogin, user.id, user.userName, scimEmail,
- scimGivenName, scimFamilyName,
- null, // Can't fetch it from SCIMUser endpoint
- attributesToGet, allowPartialAttributeValues, queryPageSize);
- }
-
- private ConnectorObject toConnectorObject(GitHubSchema schema, String queryLogin, GraphQLExternalIdentityEdge user,
- Set attributesToGet, boolean allowPartialAttributeValues, int queryPageSize) {
- GraphQLExternalIdentityScimAttributes scimAttrs = user.node.scimIdentity;
-
- final String scimEmail = (scimAttrs.emails != null && scimAttrs.emails.length > 0) ? scimAttrs.emails[0].value : null;
- final String login = user.node.user != null ? user.node.user.login : null;
-
- return toConnectorObject(schema, queryLogin, user.node.guid, scimAttrs.username, scimEmail,
- scimAttrs.givenName, scimAttrs.familyName,
- login,
- attributesToGet, allowPartialAttributeValues, queryPageSize);
- }
-
- private ConnectorObject toConnectorObject(GitHubSchema schema, String queryLogin, String scimUserId, String scimUserName, String scimEmail,
- String scimGivenName, String scimFamilyName,
- String login,
- Set attributesToGet, boolean allowPartialAttributeValues, int queryPageSize) {
- final ConnectorObjectBuilder builder = new ConnectorObjectBuilder()
- .setObjectClass(USER_OBJECT_CLASS)
- // Always returns "scimUserId"
- .setUid(scimUserId);
-
- // Always returns "_unknown_:" or ":" as NAME
- String userNameValue = resolveUserLogin(queryLogin, login, scimUserName);
- builder.setName(userNameValue);
-
- // Attributes
- if (shouldReturn(attributesToGet, ATTR_SCIM_EMAIL) &&
- scimEmail != null) {
- builder.addAttribute(ATTR_SCIM_EMAIL, scimEmail);
- }
- if (shouldReturn(attributesToGet, ATTR_SCIM_GIVEN_NAME) &&
- scimGivenName != null) {
- builder.addAttribute(ATTR_SCIM_GIVEN_NAME, scimGivenName);
- }
- if (shouldReturn(attributesToGet, ATTR_SCIM_FAMILY_NAME) &&
- scimFamilyName != null) {
- builder.addAttribute(ATTR_SCIM_FAMILY_NAME, scimFamilyName);
- }
-
- String userLogin = getUserLogin(userNameValue);
-
- // Readonly
- // We need to return user login always because it causes duplicate NAME if we don't return.
- // IDM detects no data, then try to update NAME.
- builder.addAttribute(ATTR_USER_LOGIN, userLogin);
-
- if (shouldReturn(attributesToGet, ATTR_SCIM_USER_NAME) &&
- scimUserName != null) {
- builder.addAttribute(ATTR_SCIM_USER_NAME, scimUserName);
- }
-
- if (allowPartialAttributeValues) {
- // Suppress fetching associations because they cost time and resource, also it consumes rate limit
- LOGGER.ok("[{0}] Suppress fetching associations because return partial attribute values is requested", instanceName);
-
- Stream.of(ATTR_TEAMS, ATTR_MAINTAINER_TEAMS, ATTR_ORGANIZATION_ROLE).forEach(attrName -> {
- AttributeBuilder ab = new AttributeBuilder();
- ab.setName(attrName).setAttributeValueCompleteness(AttributeValueCompleteness.INCOMPLETE);
- ab.addValue(Collections.EMPTY_LIST);
- builder.addAttribute(ab.build());
- });
-
- return builder.build();
- }
-
- if (attributesToGet == null) {
- // Suppress fetching associations default
- LOGGER.ok("[{0}] Suppress fetching associations because returned by default is true", instanceName);
-
- return builder.build();
- }
-
- if (userLogin.equals(UNKNOWN_USER_NAME)) {
- LOGGER.ok("[{0}] Suppress fetching associations because the user isn't complete the invitation", instanceName);
-
- return builder.build();
- }
-
- // Fetching associations if needed
-
- if (shouldReturn(attributesToGet, ATTR_TEAMS) || shouldReturn(attributesToGet, ATTR_MAINTAINER_TEAMS)) {
- // Fetch teams
- LOGGER.ok("[{0}] Fetching teams/maintainer teams because attributes to get is requested", instanceName);
-
- try {
- // Fetch teams by user's login name
- // It's supported by GraphQL API only...
- // If the user is not found in the organization (leave by self or change their login name), the GraphAPI returns all teams unfortunately.
- // That's why we do filtering by totalCount == 1 here.
- List allTeams = orgApiClient.listTeams(userLogin, queryPageSize)
- .toList().stream()
- .filter(t -> t.node.members.totalCount == 1)
- .collect(Collectors.toList());
-
- List memberTeams = allTeams.stream()
- .filter(t -> t.node.members.edges[0].role == GraphQLTeamMemberRole.MEMBER)
- .map(GitHubUtils::toTeamUid)
- .collect(Collectors.toList());
-
- List maintainerTeams = allTeams.stream()
- .filter(t -> t.node.members.edges[0].role == GraphQLTeamMemberRole.MAINTAINER)
- .map(GitHubUtils::toTeamUid)
- .collect(Collectors.toList());
-
- builder.addAttribute(ATTR_TEAMS, memberTeams);
- builder.addAttribute(ATTR_MAINTAINER_TEAMS, maintainerTeams);
-
- } catch (IOException ignore) {
- LOGGER.warn("Failed to fetch GitHub organization membership for user: {0}, error: {1}", userLogin, ignore.getMessage());
- // Ignore the error, IDM try to reconcile the memberships
- }
- }
-
- if (shouldReturn(attributesToGet, ATTR_ORGANIZATION_ROLE)) {
- try {
- GHMembership membership = orgApiClient.getOrganizationMembership(userLogin);
- builder.addAttribute(ATTR_ORGANIZATION_ROLE, membership.getRole().name().toLowerCase());
-
- } catch (IOException ignore) {
- // If the user is not found (leave by self or change their login name), IDM will do discovery process
- LOGGER.warn("Failed to fetch GitHub organization membership for user: {0}, error: {1}", userLogin, ignore.getMessage());
- // Ignore the error, IDM try to reconcile the memberships
- }
- }
-
- return builder.build();
- }
-
- private String resolveUserLogin(String queryLogin, String login, String scimUserName) {
- if (login != null) {
- return toUserName(login, scimUserName);
- }
- if (queryLogin != null) {
- return toUserName(queryLogin, scimUserName);
- }
- return toUserName(null, scimUserName);
- }
-
- @Override
- public boolean isOrganizationMember(String userLogin) {
- return withAuth(() -> {
- return orgApiClient.isMember(userLogin);
- });
- }
-
- @Override
- public void assignOrganizationRole(String userLogin, String organizationRole) {
- withAuth(() -> {
- try {
- GHOrganization.Role role = GHOrganization.Role.valueOf(organizationRole.toUpperCase());
-
- orgApiClient.setOrganizationMembership(userLogin, role);
-
- } catch (IllegalArgumentException e) {
- throw new InvalidAttributeValueException("Invalid organizationRole: " + organizationRole);
- }
-
- return null;
- });
- }
-
- @Override
- public void assignTeams(String login, String teamRole, Collection teams) {
- withAuth(() -> {
- for (String team : teams) {
- try {
- GHTeam.Role role = GHTeam.Role.valueOf(teamRole.toUpperCase());
-
- orgApiClient.addTeamMembership(getTeamDatabaseId(team), login, role);
-
- } catch (IllegalArgumentException e) {
- throw new InvalidAttributeValueException("Invalid teamRole: " + teamRole);
- }
- }
-
- return null;
- });
- }
-
- @Override
- public void unassignTeams(String login, Collection teams) {
- withAuth(() -> {
- for (String team : teams) {
- orgApiClient.removeTeamMembership(getTeamDatabaseId(team), login);
- }
-
- return null;
- });
- }
-
- @Override
- public Uid createTeam(GitHubSchema schema, String teamName, String description, String privacy, Long parentTeamDatabaseId) throws AlreadyExistsException {
- return withAuth(() -> {
- GHTeamBuilder builder = orgApiClient.createTeam(teamName);
-
- if (description != null) {
- builder.description(description);
- }
- if (privacy != null) {
- GHTeam.Privacy ghPrivacy = toGHTeamPrivacy(privacy);
- builder.privacy(ghPrivacy);
- }
- if (parentTeamDatabaseId != null) {
- builder.parentTeamId(parentTeamDatabaseId);
- }
-
- GHTeam created = builder.create();
-
- // To use for REST API and GraphQL API, we combine databaseId and nodeId
- return new Uid(toTeamUid(created), new Name(created.getName()));
- });
- }
-
- @Override
- public Uid updateTeam(GitHubSchema schema, Uid uid, String teamName, String description, String privacy, Long parentTeamId,
- boolean clearParent, OperationOptions options) throws UnknownUidException {
- return withAuth(() -> {
- GHTeam.Privacy ghPrivacy = null;
- if (privacy != null) {
- ghPrivacy = toGHTeamPrivacy(privacy);
- }
-
- GHTeam updated = orgApiClient.updateTeam(getTeamDatabaseId(uid), teamName, description, ghPrivacy, parentTeamId, clearParent);
-
- return new Uid(toTeamUid(updated), new Name(updated.getName()));
- });
- }
-
- @Override
- public void deleteTeam(GitHubSchema schema, Uid uid, OperationOptions options) throws UnknownUidException {
- withAuth(() -> {
- orgApiClient.deleteTeam(getTeamDatabaseId(uid));
-
- return null;
- });
- }
-
- @Override
- public void getTeams(GitHubSchema schema, ResultsHandler handler, OperationOptions options, Set attributesToGet, boolean allowPartialAttributeValues, int queryPageSize) {
- withAuth(() -> {
- orgApiClient.listTeamsExt().withPageSize(queryPageSize)
- .forEach(t -> {
- handler.handle(toTeamConnectorObject(schema, t, attributesToGet, allowPartialAttributeValues, queryPageSize));
- });
-
- return null;
- });
- }
-
- @Override
- public void getTeam(GitHubSchema schema, Uid uid, ResultsHandler handler, OperationOptions options, Set attributesToGet, boolean allowPartialAttributeValues, int queryPageSize) {
- withAuth(() -> {
- GHTeamExt team = orgApiClient.getTeam(getTeamDatabaseId(uid));
-
- handler.handle(toTeamConnectorObject(schema, team, attributesToGet, allowPartialAttributeValues, queryPageSize));
-
- return null;
- });
- }
-
- @Override
- public void getTeam(GitHubSchema schema, Name name, ResultsHandler handler, OperationOptions options, Set attributesToGet, boolean allowPartialAttributeValues, int queryPageSize) {
- withAuth(() -> {
- PagedIterator iter = orgApiClient.findTeam(name.getNameValue(), queryPageSize).iterator();
- while (iter.hasNext()) {
- GraphQLTeamEdge team = iter.next();
- if (team.node.name.equalsIgnoreCase(name.getNameValue())) {
- // Found
- handler.handle(toTeamConnectorObject(schema, team, attributesToGet, allowPartialAttributeValues, queryPageSize));
-
- break;
- }
- }
-
- return null;
- });
- }
-
- private ConnectorObject toTeamConnectorObject(GitHubSchema schema, GHTeamExt team, Set attributesToGet, boolean allowPartialAttributeValues, long queryPageSize) {
- String teamId = toTeamUid(team);
-
- String parentId = null;
- if (team.getParent() != null) {
- parentId = toTeamUid(team.getParent());
- }
-
- GraphQLTeamPrivacy privacy;
- if (team.getPrivacy() == GHTeam.Privacy.SECRET.SECRET) {
- privacy = GraphQLTeamPrivacy.SECRET;
- } else {
- privacy = GraphQLTeamPrivacy.VISIBLE;
- }
-
- return toTeamConnectorObject(schema, teamId, team.getId(), team.getNodeId(), team.getName(), team.getSlug(),
- team.getDescription(), privacy, parentId,
- attributesToGet, allowPartialAttributeValues, queryPageSize);
- }
-
- private ConnectorObject toTeamConnectorObject(GitHubSchema schema, GraphQLTeamEdge teamEdge, Set attributesToGet, boolean allowPartialAttributeValues, long queryPageSize) {
- GraphQLTeam team = teamEdge.node;
-
- String teamId = toTeamUid(team);
-
- String parentId = null;
- if (team.parentTeam != null) {
- parentId = toTeamUid(team.parentTeam);
- }
-
- return toTeamConnectorObject(schema, teamId, team.databaseId, team.id, team.name, team.slug,
- team.description, team.privacy, parentId,
- attributesToGet, allowPartialAttributeValues, queryPageSize);
- }
-
- private ConnectorObject toTeamConnectorObject(GitHubSchema schema, String teamId, long databaseId, String nodeId, String teamName,
- String slug, String description, GraphQLTeamPrivacy privacy, String parentId,
- Set attributesToGet, boolean allowPartialAttributeValues, long queryPageSize) {
- final ConnectorObjectBuilder builder = new ConnectorObjectBuilder()
- .setObjectClass(TEAM_OBJECT_CLASS)
- // Always returns "teamId"
- .setUid(teamId)
- // Always returns "slug"
- .setName(teamName);
-
- // Attributes
- if (shouldReturn(attributesToGet, ATTR_DESCRIPTION) &&
- !StringUtil.isEmpty(description)) {
- builder.addAttribute(ATTR_DESCRIPTION, description);
- }
- if (shouldReturn(attributesToGet, ATTR_PRIVACY)) {
- builder.addAttribute(ATTR_PRIVACY, privacy.name().toLowerCase());
- }
- if (shouldReturn(attributesToGet, ATTR_PARENT_TEAM_ID) &&
- parentId != null) {
- builder.addAttribute(ATTR_PARENT_TEAM_ID, parentId);
- }
-
- // Readonly
- if (shouldReturn(attributesToGet, ATTR_TEAM_DATABASE_ID)) {
- builder.addAttribute(ATTR_TEAM_DATABASE_ID, databaseId);
- }
- if (shouldReturn(attributesToGet, ATTR_SLUG)) {
- builder.addAttribute(ATTR_SLUG, slug);
- }
- if (shouldReturn(attributesToGet, ATTR_TEAM_NODE_ID)) {
- builder.addAttribute(ATTR_TEAM_NODE_ID, nodeId);
- }
-
- return builder.build();
- }
-
- @Override
- public void close() {
- }
-
- private static PrivateKey get(String privateKeyPEM) {
- Optional keySpec = PKCS1PEMKey.loadKeySpec(privateKeyPEM.getBytes());
-
- if (!keySpec.isPresent()) {
- throw new ConnectionFailedException("Failed to load private key PEM");
- }
-
- try {
- KeyFactory kf = KeyFactory.getInstance("RSA");
- return kf.generatePrivate(keySpec.get());
- } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
- throw new ConnectionFailedException("Failed to load the privateKey from the configuration", e);
- }
- }
-
- public static String createJWT(String githubAppId, long ttlMillis, String privateKeyPEM) {
- //The JWT signature algorithm we will be using to sign the token
- SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.RS256;
-
- long nowMillis = System.currentTimeMillis();
- Date now = new Date(nowMillis);
-
- //We will sign our JWT with our private key
- Key signingKey = get(privateKeyPEM);
-
- //Let's set the JWT Claims
- JwtBuilder builder = Jwts.builder()
- .setIssuedAt(now)
- .setIssuer(githubAppId)
- .signWith(signingKey, signatureAlgorithm);
-
- //if it has been specified, let's add the expiration
- if (ttlMillis > 0) {
- long expMillis = nowMillis + ttlMillis;
- Date exp = new Date(expMillis);
- builder.setExpiration(exp);
- }
-
- //Builds the JWT and serializes it to a compact, URL-safe string
- return builder.compact();
- }
-}
diff --git a/src/main/java/jp/openstandia/connector/util/SchemaDefinition.java b/src/main/java/jp/openstandia/connector/util/SchemaDefinition.java
index acf209b..35dedd0 100644
--- a/src/main/java/jp/openstandia/connector/util/SchemaDefinition.java
+++ b/src/main/java/jp/openstandia/connector/util/SchemaDefinition.java
@@ -250,10 +250,6 @@ private Map buildAttributeMap() {
.collect(Collectors.toMap(a -> a.connectorName, a -> a));
return map;
}
-
- public void addEnable() {
-
- }
}
private final ObjectClass objectClass;
diff --git a/src/main/java/org/kohsuke/github/GHEnterpriseExt.java b/src/main/java/org/kohsuke/github/GHEnterpriseExt.java
index 95c0a9a..a037b89 100644
--- a/src/main/java/org/kohsuke/github/GHEnterpriseExt.java
+++ b/src/main/java/org/kohsuke/github/GHEnterpriseExt.java
@@ -155,6 +155,32 @@ public SCIMEMUGroup getSCIMEMUGroupByDisplayName(String scimGroupDisplayName) th
return list.get(0);
}
+ public GitHubCopilotSeat getCopilotSeatByDisplayName(String copilotSeatDisplayName) throws IOException {
+ List allSeats = searchCopilotSeats()
+ .list()
+ .toList();
+
+ if (allSeats.isEmpty()) {
+ return null;
+ }
+
+ return allSeats.stream()
+ .filter(seat -> copilotSeatDisplayName.equals(seat.assignee.login))
+ .findFirst()
+ .orElse(null);
+ }
+
+ public GitHubCopilotSeat getCopilotSeatByUid(String copilotSeatUid) throws IOException {
+ List allSeats = searchCopilotSeats()
+ .list()
+ .toList();
+
+ return allSeats.stream()
+ .filter(seat -> copilotSeatUid.equals(seat.assignee.id))
+ .findFirst()
+ .orElse(null);
+ }
+
/**
* Search groups.
*
@@ -169,6 +195,15 @@ public SCIMPagedSearchIterable listSCIMGroups(int pageSize, int pa
return searchSCIMGroups().list().withPageSize(pageSize).withPageOffset(pageOffset);
}
+ public GitHubCopilotSeatsSearchBuilder searchCopilotSeats() {
+ return new GitHubCopilotSeatsSearchBuilder(root, this);
+ }
+
+ public GitHubCopilotSeatPagedSearchIterable listAllSeats(int pageSize, int pageOffset)
+ throws IOException {
+ return searchCopilotSeats().list().withPageSize(pageSize).withPageOffset(pageOffset);
+ }
+
public void deleteSCIMGroup(String scimGroupId) throws IOException {
root.createRequest()
.method("DELETE")
diff --git a/src/main/java/org/kohsuke/github/GHOrganizationExt.java b/src/main/java/org/kohsuke/github/GHOrganizationExt.java
deleted file mode 100644
index 06ec05f..0000000
--- a/src/main/java/org/kohsuke/github/GHOrganizationExt.java
+++ /dev/null
@@ -1,257 +0,0 @@
-package org.kohsuke.github;
-
-import com.fasterxml.jackson.databind.ObjectMapper;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Extends original GHOrganization class.
- *
- * @author Hiroyuki Wada
- */
-public class GHOrganizationExt extends GHOrganization {
- private static final ObjectMapper mapper = new ObjectMapper();
-
- @Override
- GHOrganizationExt wrapUp(GitHub root) {
- return (GHOrganizationExt) super.wrapUp(root);
- }
-
- public GHUser createInvitation(String email, String role) throws IOException {
- return root.createRequest()
- .method("POST")
- .withHeader("Accept", "application/vnd.github.v3+json")
- .with("email", email)
- .with("role", role)
- .withUrlPath(String.format("/orgs/%s/invitations", login))
- .fetch(GHUser.class);
- }
-
- public Iterable listInvitation() {
- return root.createRequest()
- .withUrlPath(String.format("/orgs/%s/invitations", login))
- .toIterable(GHUser[].class, item -> item.wrapUp(root));
- }
-
- public SCIMUser createSCIMUser(SCIMUser newUser) throws IOException {
- Map map = new HashMap<>();
- map.put("userName", newUser.userName);
- map.put("emails", newUser.emails);
- map.put("name", newUser.name);
- map.put("externalId", newUser.externalId);
-
- SCIMUser u = root.createRequest()
- .method("POST")
- .withHeader(SCIMConstants.HEADER_ACCEPT, SCIMConstants.SCIM_ACCEPT)
- .withHeader(SCIMConstants.GITHUB_API_VERSION, SCIMConstants.GITHUB_API_VERSION)
- .with(map)
- .withUrlPath(String.format("/scim/v2/organizations/%s/Users", login))
- .fetch(SCIMUser.class);
- return u;
- }
-
- public SCIMUser updateSCIMUser(String scimUserId, String scimUserName, String scimEmail, String scimGivenName, String scimFamilyName) throws IOException {
- List ops = new ArrayList<>();
-
- if (scimUserName != null) {
- ops.add(new SCIMOperation<>("replace", "userName", scimUserName));
- }
- if (scimEmail != null) {
- List