Skip to content

Commit

Permalink
OAK-10604 - Azure Service Principal Support in oak-segment-azure (#1268)
Browse files Browse the repository at this point in the history
* OAK-10604 - Azure Service Principal Support in oak-segment-azure
Added service principal configuration properties
Added Azure Identity client library for Java as a dependency and updated all oak-run* poms to adjust size

* OAK-10604 - Azure Service Principal Support in oak-segment-azure
First untested implementation

* OAK-10604 - Azure Service Principal Support in oak-segment-azure
Added scope to token request
Added test case using environment variables

* OAK-10604 - Azure Service Principal Support in oak-segment-azure
Nitpick - avoid wildcard imports

* OAK-10604 - Azure Service Principal Support in oak-segment-azure
Corrected token request scope and fixed the test

* OAK-10604 - Azure Service Principal Support in oak-segment-azure
OSGi wiring fix - wip

* OSGi wiring - wip

* OAK-10604 - Azure Service Principal Support in oak-segment-azure
Fixed OSGi wiring

* OAK-10604 - Azure Service Principal Support in oak-segment-azure
Increased size for oak-run and oak-run-elastic jars after embedding more libraries in oak-segment-azure

* OAK-10604 - Azure Service Principal Support in oak-segment-azure
Increased again oak-run jar size due to sonar complaining about it during CI
  • Loading branch information
dulceanu authored Jan 22, 2024
1 parent aaccd62 commit 5389ea4
Show file tree
Hide file tree
Showing 8 changed files with 209 additions and 36 deletions.
25 changes: 25 additions & 0 deletions oak-parent/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -721,6 +721,31 @@
<artifactId>azure-keyvault-core</artifactId>
<version>1.2.6</version>
</dependency>
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-identity</artifactId>
<version>1.11.1</version>
</dependency>
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-identity-broker</artifactId>
<version>1.0.1</version>
</dependency>
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-core</artifactId>
<version>1.45.1</version>
</dependency>
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-json</artifactId>
<version>1.0.1</version>
</dependency>
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-xml</artifactId>
<version>1.0.0-beta.2</version>
</dependency>

<!-- Pax Exam Integration Test Dependencies -->
<dependency>
Expand Down
3 changes: 2 additions & 1 deletion oak-run-elastic/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,9 @@
121 MB : add Elasticsearch Java client along with RHLC: the latter can be removed when the code can be fully migrated to use the new client
125 MB : shaded Guava
85 MB : remove Elasticsearch RHLC
103.5 MB: Azure Identity client library for Java (OAK-10604)
-->
<max.jar.size>85000000</max.jar.size>
<max.jar.size>103500000</max.jar.size>

</properties>

Expand Down
3 changes: 2 additions & 1 deletion oak-run/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
<jetty.version>9.4.53.v20231009</jetty.version>
<!--
Size History:
+ 78 MB Azure Identity client library for Java (OAK-10604)
+ 60 MB Groovy 2.5 (OAK-10066)
+ 56 MB MongoDB Java driver 3.12.7 (OAK-9357)
+ 55 MB Add support for segment persistent cache (OAK-7744)
Expand All @@ -47,7 +48,7 @@
+ 41 MB build failing on the release profile (OAK-6250)
+ 38 MB. Initial value. Current 35MB plus a 10%
-->
<max.jar.size>62914560</max.jar.size>
<max.jar.size>78100000</max.jar.size>
</properties>

<build>
Expand Down
55 changes: 54 additions & 1 deletion oak-segment-azure/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,24 @@
<Import-Package>
org.apache.jackrabbit.oak.segment.spi*,
org.apache.jackrabbit.oak.segment.remote*,
com.fasterxml.jackson.annotation;resolution:=optional,
com.fasterxml.jackson.databind*;resolution:=optional,
com.fasterxml.jackson.dataformat.xml;resolution:=optional,
com.fasterxml.jackson.datatype*;resolution:=optional,
com.microsoft.aad.msal4j*;resolution:=optional,
com.nimbusds.common.contenttype;resolution:=optional,
com.nimbusds.jose*;resolution:=optional,
com.nimbusds.jwt;resolution:=optional,
com.nimbusds.jwt.util;resolution:=optional,
com.nimbusds.oauth2.sdk*;resolution:=optional,
com.nimbusds.jwt.proc;resolution:=optional,
com.nimbusds.langtag;resolution:=optional,
com.nimbusds.openid.connect.sdk*;resolution:=optional,
com.nimbusds.secevent.sdk*;resolution:=optional,
com.sun.jna*;resolution:=optional,
org.reactivestreams;resolution:=optional,
reactor.core*;resolution:=optional,
reactor.util*;resolution:=optional,
!org.apache.jackrabbit.oak.segment*,
!com.google.*,
!android.os,
Expand All @@ -54,11 +72,18 @@
org.apache.jackrabbit.oak.segment.azure.util,
com.microsoft.azure.storage,
com.microsoft.azure.storage.core,
com.microsoft.azure.storage.blob
com.microsoft.azure.storage.blob,
com.azure.core.credential,
com.azure.identity
</Export-Package>
<Embed-Dependency>
azure-storage,
azure-keyvault-core,
azure-core,
azure-identity,
azure-identity-broker,
azure-json,
azure-xml,
guava,
jsr305
</Embed-Dependency>
Expand Down Expand Up @@ -151,6 +176,34 @@
<groupId>com.microsoft.azure</groupId>
<artifactId>azure-keyvault-core</artifactId>
</dependency>

<!-- Azure Identity Client for Microsoft Entra ID dependency -->
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-identity</artifactId>
</dependency>
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-identity-broker</artifactId>
</dependency>
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-core</artifactId>
</dependency>
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-json</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-xml</artifactId>
</dependency>


<!-- Azure Guava dependency -->
<dependency>
<groupId>com.google.guava</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,12 @@
*/
package org.apache.jackrabbit.oak.segment.azure;

import com.azure.core.credential.TokenRequestContext;
import com.azure.identity.ClientSecretCredential;
import com.azure.identity.ClientSecretCredentialBuilder;
import com.microsoft.azure.storage.CloudStorageAccount;
import com.microsoft.azure.storage.LocationMode;
import com.microsoft.azure.storage.StorageCredentialsToken;
import com.microsoft.azure.storage.StorageException;
import com.microsoft.azure.storage.blob.BlobRequestOptions;
import com.microsoft.azure.storage.blob.CloudBlobClient;
Expand All @@ -41,7 +45,6 @@
import java.security.InvalidKeyException;
import java.util.Hashtable;
import java.util.Objects;
import java.util.Properties;

import static org.osgi.framework.Constants.SERVICE_PID;

Expand All @@ -57,6 +60,7 @@ public class AzureSegmentStoreService {
public static final String DEFAULT_ROOT_PATH = "/oak";

public static final boolean DEFAULT_ENABLE_SECONDARY_LOCATION = false;
public static final String DEFAULT_ENDPOINT_SUFFIX = "core.windows.net";

private ServiceRegistration registration;

Expand Down Expand Up @@ -84,6 +88,9 @@ private static AzurePersistence createAzurePersistenceFrom(Configuration configu
if (!StringUtils.isBlank(configuration.connectionURL())) {
return createPersistenceFromConnectionURL(configuration);
}
if (!StringUtils.isAnyBlank(configuration.clientId(), configuration.clientSecret(), configuration.tenantId())) {
return createPersistenceFromServicePrincipalCredentials(configuration);
}
if (!StringUtils.isBlank(configuration.sharedAccessSignature())) {
return createPersistenceFromSasUri(configuration);
}
Expand Down Expand Up @@ -118,33 +125,53 @@ private static AzurePersistence createPersistenceFromConnectionURL(Configuration
}

@NotNull
private static AzurePersistence createAzurePersistence(
String connectionString,
Configuration configuration,
boolean createContainer
) throws IOException {
private static AzurePersistence createPersistenceFromServicePrincipalCredentials(Configuration configuration) throws IOException {
ClientSecretCredential clientSecretCredential = new ClientSecretCredentialBuilder()
.clientId(configuration.clientId())
.clientSecret(configuration.clientSecret())
.tenantId(configuration.tenantId())
.build();

String accessToken = clientSecretCredential.getTokenSync(new TokenRequestContext().addScopes("https://storage.azure.com/.default")).getToken();
StorageCredentialsToken storageCredentialsToken = new StorageCredentialsToken(configuration.accountName(), accessToken);

try {
CloudStorageAccount cloud = new CloudStorageAccount(storageCredentialsToken, true, DEFAULT_ENDPOINT_SUFFIX, configuration.accountName());
return createAzurePersistence(cloud, configuration, true);
} catch (StorageException | URISyntaxException e) {
throw new IOException(e);
}
}

@NotNull
private static AzurePersistence createAzurePersistence(String connectionString, Configuration configuration, boolean createContainer) throws IOException {
try {
CloudStorageAccount cloud = CloudStorageAccount.parse(connectionString);
log.info("Connection string: '{}'", cloud);
CloudBlobClient cloudBlobClient = cloud.createCloudBlobClient();
BlobRequestOptions blobRequestOptions = new BlobRequestOptions();

if (configuration.enableSecondaryLocation()) {
blobRequestOptions.setLocationMode(LocationMode.PRIMARY_THEN_SECONDARY);
}
cloudBlobClient.setDefaultRequestOptions(blobRequestOptions);

CloudBlobContainer container = cloudBlobClient.getContainerReference(configuration.containerName());
if (createContainer && !container.exists()) {
container.create();
}
String path = normalizePath(configuration.rootPath());
return new AzurePersistence(container.getDirectoryReference(path));
return createAzurePersistence(cloud, configuration, createContainer);
} catch (StorageException | URISyntaxException | InvalidKeyException e) {
throw new IOException(e);
}
}

@NotNull
private static AzurePersistence createAzurePersistence(CloudStorageAccount cloud, Configuration configuration, boolean createContainer) throws URISyntaxException, StorageException {
CloudBlobClient cloudBlobClient = cloud.createCloudBlobClient();
BlobRequestOptions blobRequestOptions = new BlobRequestOptions();

if (configuration.enableSecondaryLocation()) {
blobRequestOptions.setLocationMode(LocationMode.PRIMARY_THEN_SECONDARY);
}
cloudBlobClient.setDefaultRequestOptions(blobRequestOptions);

CloudBlobContainer container = cloudBlobClient.getContainerReference(configuration.containerName());
if (createContainer && !container.exists()) {
container.create();
}
String path = normalizePath(configuration.rootPath());
return new AzurePersistence(container.getDirectoryReference(path));
}

@NotNull
private static String normalizePath(@NotNull String rootPath) {
if (rootPath.length() > 0 && rootPath.charAt(0) == '/') {
Expand All @@ -153,5 +180,4 @@ private static String normalizePath(@NotNull String rootPath) {
return rootPath;
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,21 @@
description = "Blob Endpoint URL used to connect to the Azure Storage")
String blobEndpoint() default "";

@AttributeDefinition(
name = "Azure Service Principal ID (optional)",
description = "Azure Service Principal ID for Azure Storage authentication")
String clientId() default "";

@AttributeDefinition(
name = "Azure Service Principal Password (optional)",
description = "Azure Service Principal Password for Azure Storage authentication")
String clientSecret() default "";

@AttributeDefinition(
name = "Azure Active Directory ID (optional)",
description = "Azure Active Directory ID for Azure Storage authentication")
String tenantId() default "";

@AttributeDefinition(
name = "Role",
description = "The role of this persistence. It should be unique and may be used to filter " +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
* limitations under the License.
*/
@Internal(since = "1.0.0")
@Version("2.0.0")
@Version("2.1.0")
package org.apache.jackrabbit.oak.segment.azure;

import org.apache.jackrabbit.oak.commons.annotations.Internal;
Expand Down
Loading

0 comments on commit 5389ea4

Please sign in to comment.