Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 1 addition & 10 deletions .github/workflows/gradle-build-development.yml
Original file line number Diff line number Diff line change
Expand Up @@ -98,16 +98,7 @@ jobs:
--set-env-vars "OAUTH_CLIENT_SECRET=${{ secrets.OAUTH_CLIENT_SECRET }}" \
--set-env-vars "OAUTH_CALLBACK_URI"=${{ secrets.OAUTH_CALLBACK_URI }} \
--set-env-vars "DIRECTORY_ID=${{ secrets.DIRECTORY_ID }}" \
--set-env-vars "TYPE=${{ secrets.SA_KEY_TYPE }}" \
--set-env-vars "PROJECT_ID=${{ secrets.RUN_PROJECT }}" \
--set-env-vars "PRIVATE_KEY_ID=${{ secrets.SA_PRIVATE_KEY_ID }}" \
--set-env-vars "PRIVATE_KEY=${{ secrets.SA_PRIVATE_KEY }}" \
--set-env-vars "CLIENT_EMAIL=${{ secrets.SA_CLIENT_EMAIL }}" \
--set-env-vars "CLIENT_ID=${{ secrets.SA_CLIENT_ID }}" \
--set-env-vars "AUTH_URI=${{ secrets.SA_AUTH_URI }}" \
--set-env-vars "TOKEN_URI=${{ secrets.SA_TOKEN_URI }}" \
--set-env-vars "AUTH_PROVIDER_X509_CERT_URL=${{ secrets.SA_AUTH_PROVIDER_X509_CERT_URL }}" \
--set-env-vars "CLIENT_X509_CERT_URL=${{ secrets.SA_CLIENT_X509_CERT_URL }}" \
--set-env-vars "SERVICE_ACCOUNT_CREDENTIALS=${{ secrets.SERVICE_ACCOUNT_CREDENTIALS }}" \
--set-env-vars "GSUITE_SUPER_ADMIN=${{ secrets.GSUITE_SUPER_ADMIN }}" \
--set-env-vars "MJ_APIKEY_PUBLIC=${{ secrets.MJ_APIKEY_PUBLIC }}" \
--set-env-vars "MJ_APIKEY_PRIVATE=${{ secrets.MJ_APIKEY_PRIVATE }}" \
Expand Down
Original file line number Diff line number Diff line change
@@ -1,171 +1,88 @@
package com.objectcomputing.checkins.security;

import io.micronaut.context.annotation.ConfigurationProperties;
import io.micronaut.core.annotation.Introspected;
import io.micronaut.context.annotation.Factory;
import io.micronaut.core.type.Argument;
import io.micronaut.json.JsonMapper;
import io.micronaut.validation.validator.constraints.ConstraintValidator;
import jakarta.inject.Singleton;
import jakarta.validation.Constraint;
import jakarta.validation.constraints.NotNull;

import lombok.Getter;
import lombok.Setter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.Base64;
import java.util.Map;
import java.util.stream.Stream;

@Getter
@Setter
@ConfigurationProperties("service-account-credentials")
@Introspected
public class GoogleServiceConfiguration {

@NotNull
public String directory_id;

@NotNull
public String type;

@NotNull
public String project_id;

@NotNull
public String private_key_id;

@NotNull
public String private_key;

@NotNull
public String client_email;

@NotNull
public String client_id;

@NotNull
public String auth_uri;

@NotNull
public String token_uri;

@NotNull
public String auth_provider_x509_cert_url;
private static final Logger LOG = LoggerFactory.getLogger(GoogleServiceConfiguration.class);

@NotNull
public String client_x509_cert_url;

@NotNull
public String oauth_client_id;

@NotNull
public String oauth_client_secret;

public String getDirectory_id() {
return directory_id;
}

public void setDirectory_id(String directory_id) {
this.directory_id = directory_id;
}

public String getType() {
return type;
}

public void setType(String type) {
this.type = type;
}

public String getProject_id() {
return project_id;
}

public void setProject_id(String project_id) {
this.project_id = project_id;
}

public String getPrivate_key_id() {
return private_key_id;
}

public void setPrivate_key_id(String private_key_id) {
this.private_key_id = private_key_id;
}

public String getPrivate_key() {
return private_key;
}

public void setPrivate_key(String private_key) {
this.private_key = private_key;
}

public String getClient_email() {
return client_email;
}

public void setClient_email(String client_email) {
this.client_email = client_email;
}

public String getClient_id() {
return client_id;
}

public void setClient_id(String client_id) {
this.client_id = client_id;
}

public String getAuth_uri() {
return auth_uri;
}

public void setAuth_uri(String auth_uri) {
this.auth_uri = auth_uri;
}

public String getToken_uri() {
return token_uri;
}

public void setToken_uri(String token_uri) {
this.token_uri = token_uri;
}

public String getAuth_provider_x509_cert_url() {
return auth_provider_x509_cert_url;
}

public void setAuth_provider_x509_cert_url(String auth_provider_x509_cert_url) {
this.auth_provider_x509_cert_url = auth_provider_x509_cert_url;
}

public String getClient_x509_cert_url() {
return client_x509_cert_url;
}

public void setClient_x509_cert_url(String client_x509_cert_url) {
this.client_x509_cert_url = client_x509_cert_url;
}

public String getOauth_client_id() {
return oauth_client_id;
}

public void setOauth_client_id(String oauth_client_id) {
this.oauth_client_id = oauth_client_id;
}

public String getOauth_client_secret() {
return oauth_client_secret;
}

public void setOauth_client_secret(String oauth_client_secret) {
this.oauth_client_secret = oauth_client_secret;
}

public String toString() {
return "{" +
"\"directory_id\":\"" + directory_id +
"\", \"type\":\"" + type +
"\", \"project_id\":\"" + project_id +
"\", \"private_key_id\":\"" + private_key_id +
"\", \"private_key\":\"" + private_key +
"\", \"client_email\":\"" + client_email +
"\", \"client_id\":\"" + client_id +
"\", \"auth_uri\":\"" + auth_uri +
"\", \"token_uri\":\"" + token_uri +
"\", \"auth_provider_x509_cert_url\":\"" + auth_provider_x509_cert_url +
"\", \"client_x509_cert_url\":\"" + client_x509_cert_url +
"\", \"oauth_client_id\":\"" + oauth_client_id +
"\", \"oauth_client_secret\":\"" + oauth_client_secret +
"\"}";
private String directoryId;

@ValidEncodedGoogleServiceConfiguration
private String encodedValue;

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy = {})
@interface ValidEncodedGoogleServiceConfiguration {
}

@Factory
static class CustomValidationFactory {

private final JsonMapper jsonMapper;
private static final Base64.Decoder DECODER = Base64.getDecoder();

CustomValidationFactory(JsonMapper jsonMapper) {
this.jsonMapper = jsonMapper;
}

@Singleton
ConstraintValidator<ValidEncodedGoogleServiceConfiguration, String> e164Validator() {
return (value, annotation, context) -> {
if (value == null || !isValid(value)) {
context.buildConstraintViolationWithTemplate("must be a valid base64 encoded Google Service Configuration")
.addConstraintViolation();
}
return true;
};
}

// Check the decoded json string for the required fields
private boolean isValid(String value) {
try {
Map<String, Object> map = jsonMapper.readValue(DECODER.decode(value), Argument.mapOf(String.class, Object.class));
return Stream.of(
"type",
"project_id",
"private_key_id",
"private_key",
"client_email",
"client_id",
"auth_uri",
"token_uri",
"auth_provider_x509_cert_url",
"client_x509_cert_url"
).allMatch(map::containsKey);
} catch (Exception e) {
LOG.error("An error occurred while decoding the Google Service Configuration.", e);
}
return false;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ public Set<FileInfoDTO> findFiles(@Nullable UUID checkInID) {
Drive drive = googleApiAccess.getDrive();
validate(drive == null, "Unable to access Google Drive");

String rootDirId = googleServiceConfiguration.getDirectory_id();
String rootDirId = googleServiceConfiguration.getDirectoryId();
validate(rootDirId == null, "No destination folder has been configured. Contact your administrator for assistance.");

if (checkInID == null && isAdmin) {
Expand Down Expand Up @@ -166,7 +166,7 @@ public FileInfoDTO uploadFile(@NotNull UUID checkInID, @NotNull CompletedFileUpl
Drive drive = googleApiAccess.getDrive();
validate(drive == null, "Unable to access Google Drive");

String rootDirId = googleServiceConfiguration.getDirectory_id();
String rootDirId = googleServiceConfiguration.getDirectoryId();
validate(rootDirId == null, "No destination folder has been configured. Contact your administrator for assistance.");

// Check if folder already exists on google drive. If exists, return folderId and name
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,25 +11,27 @@
import org.slf4j.LoggerFactory;

import java.io.ByteArrayInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.List;

@Requires(notEnv = Environment.TEST)
@Singleton
public class GoogleAuthenticator {

private static final Logger LOG = LoggerFactory.getLogger(GoogleAuthenticator.class);
private static final Base64.Decoder BASE64_DECODER = Base64.getDecoder();

private GoogleServiceConfiguration gServiceConfig;
private final GoogleServiceConfiguration gServiceConfig;

/**
* Creates a google drive utility for quick access
* @param gServiceConfig, Google Drive configuration properties
*/
public GoogleAuthenticator(GoogleServiceConfiguration gServiceConfig) {
public GoogleAuthenticator(
GoogleServiceConfiguration gServiceConfig
) {
this.gServiceConfig = gServiceConfig;
}

Expand All @@ -40,14 +42,8 @@ public GoogleAuthenticator(GoogleServiceConfiguration gServiceConfig) {
* @throws IOException If the service account configurations cannot be found.
*/
GoogleCredentials setupCredentials(@NotNull final List<String> scopes) throws IOException {
InputStream in = new ByteArrayInputStream(gServiceConfig.toString().getBytes(StandardCharsets.UTF_8));
InputStream in = gcpCredentialsStream();
GoogleCredentials credentials = GoogleCredentials.fromStream(in);

if (credentials == null) {
credentials = GoogleCredentials.getApplicationDefault();
throw new FileNotFoundException("Credentials not found while using Google default credentials");
}

return scopes.isEmpty() ? credentials : credentials.createScoped(scopes);
}

Expand All @@ -56,18 +52,21 @@ GoogleCredentials setupCredentials(@NotNull final List<String> scopes) throws IO
* @param scopes, the scope(s) of access to request for this application
* @param delegatedUser, the email of the delegated user
* @return An authorized ServiceAccountCredentials object.
* @throws IOException If the service account configurations cannot be found.
*/
ServiceAccountCredentials setupServiceAccountCredentials(@NotNull final List<String> scopes, @NotNull final String delegatedUser) throws IOException {

ServiceAccountCredentials setupServiceAccountCredentials(@NotNull final List<String> scopes, @NotNull final String delegatedUser) {
ServiceAccountCredentials sourceCredentials = null;
try(InputStream in = new ByteArrayInputStream(gServiceConfig.toString().getBytes(StandardCharsets.UTF_8))) {
try {
InputStream in = gcpCredentialsStream();
sourceCredentials = ServiceAccountCredentials.fromStream(in);
sourceCredentials = (ServiceAccountCredentials) sourceCredentials.createScoped(scopes).createDelegated(delegatedUser);
} catch (IOException e) {
LOG.error("An error occurred while reading the service account credentials.", e);
}
return sourceCredentials;
}

private ByteArrayInputStream gcpCredentialsStream() {
return new ByteArrayInputStream(BASE64_DECODER.decode(gServiceConfig.getEncodedValue()));
}
}

15 changes: 2 additions & 13 deletions server/src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -111,19 +111,8 @@ flyway:
aeskey: ${ AES_KEY }
---
service-account-credentials:
directory_id: ${ DIRECTORY_ID }
type: ${ TYPE }
project_id: ${ PROJECT_ID }
private_key_id: ${ PRIVATE_KEY_ID }
private_key: ${ PRIVATE_KEY }
client_email: ${ CLIENT_EMAIL }
client_id: ${ CLIENT_ID }
auth_uri: ${ AUTH_URI }
token_uri: ${ TOKEN_URI }
auth_provider_x509_cert_url: ${ AUTH_PROVIDER_X509_CERT_URL }
client_x509_cert_url: ${ CLIENT_X509_CERT_URL }
oauth_client_id: ${ OAUTH_CLIENT_ID }
oauth_client_secret: ${ OAUTH_CLIENT_SECRET }
directory-id: ${ DIRECTORY_ID }
encoded-value: ${SERVICE_ACCOUNT_CREDENTIALS}
---
mail-jet:
from-address: ${ FROM_ADDRESS }
Expand Down
Loading