-
Notifications
You must be signed in to change notification settings - Fork 21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature/add account number verification #14
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -31,6 +31,8 @@ | |
import com.amazonaws.services.securitytoken.AWSSecurityTokenServiceClientBuilder; | ||
import com.amazonaws.services.securitytoken.model.AssumeRoleRequest; | ||
import com.google.common.base.Strings; | ||
import com.intuit.cloudraider.utils.AWSAccountValidator; | ||
|
||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
|
@@ -57,7 +59,19 @@ public class BasicCredentials implements Credentials{ | |
private String AWSSecretKey; | ||
private String AWSSessionToken; | ||
|
||
/** | ||
private String targetAccount; | ||
|
||
public String getTargetAccount() { | ||
return targetAccount; | ||
} | ||
|
||
public void setTargetAccount(String targetAccount) { | ||
this.targetAccount = targetAccount; | ||
} | ||
|
||
private AWSAccountValidator validator; | ||
|
||
/** | ||
* Reads from config.properties to search for potential AWS account credentials. | ||
* The aws.region must be specified to proceed. | ||
* A "aws.keyless" option exists and requires "aws.assumerRole", "aws.deployerRole", "aws.uuid" to be specified within | ||
|
@@ -79,7 +93,10 @@ public BasicCredentials() { | |
if(Strings.isNullOrEmpty(region)){ | ||
throw new RuntimeException("No Region defined in the configuration file"); | ||
} | ||
|
||
|
||
// Get the target account from env or properties if env var is not set | ||
targetAccount = System.getenv("aws.targetAccount") != null ? System.getenv("aws.targetAccount") : prop.getProperty("aws.targetAccount"); | ||
|
||
String profile = prop.getProperty("aws.profile"); | ||
boolean keyless = Boolean.valueOf(prop.getProperty("aws.keyless")); | ||
|
||
|
@@ -150,14 +167,24 @@ public BasicCredentials() { | |
//ignore to use amazon default provider | ||
} | ||
} | ||
|
||
public BasicCredentials(AWSAccountValidator validator) { | ||
this.validator = validator; | ||
} | ||
|
||
/** | ||
* Returns AWSCredentials for the given user/account. Reverts to default credentials provider if no credentials exist. | ||
* @return AWSCredentials | ||
*/ | ||
public AWSCredentials getAwsCredentials() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ideally, there should be one method that creates the credentials. So I'd like to consolidate that code into one method and then put the validation logic there. Does that make sense? |
||
if (awsCredentials == null) { | ||
awsCredentials = new DefaultAWSCredentialsProviderChain().getCredentials(); | ||
AWSCredentialsProvider provider = new DefaultAWSCredentialsProviderChain(); | ||
if (validator == null) { | ||
validator = new AWSAccountValidator(provider); | ||
} | ||
|
||
validator.validateAccount(targetAccount); | ||
awsCredentials = provider.getCredentials(); | ||
} | ||
return awsCredentials; | ||
} | ||
|
@@ -174,4 +201,5 @@ public String getRegion() { | |
return region; | ||
} | ||
|
||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
package com.intuit.cloudraider.utils; | ||
|
||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
import com.amazonaws.auth.AWSCredentialsProvider; | ||
import com.amazonaws.services.securitytoken.AWSSecurityTokenService; | ||
import com.amazonaws.services.securitytoken.AWSSecurityTokenServiceClientBuilder; | ||
import com.amazonaws.services.securitytoken.model.GetCallerIdentityRequest; | ||
import com.amazonaws.services.securitytoken.model.GetCallerIdentityResult; | ||
|
||
public class AWSAccountValidator { | ||
|
||
/** | ||
* The Logger. | ||
*/ | ||
Logger logger = LoggerFactory.getLogger(this.getClass()); | ||
|
||
private AWSSecurityTokenService sts; | ||
|
||
|
||
public AWSSecurityTokenService getSts() { | ||
return sts; | ||
} | ||
|
||
public void setSts(AWSSecurityTokenService sts) { | ||
this.sts = sts; | ||
} | ||
|
||
public AWSAccountValidator(AWSCredentialsProvider provider) { | ||
sts = AWSSecurityTokenServiceClientBuilder.standard().withCredentials(provider).build(); | ||
} | ||
|
||
public void validateAccount(String targetAccount) { | ||
|
||
if (targetAccount != null) { | ||
GetCallerIdentityRequest getCallerIdentityRequest = new GetCallerIdentityRequest(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the code builds the AWSCredentialsProvider on the fly, there's no real way to unit test the code without having valid credentials (ie, no way to inject a mock provider). |
||
GetCallerIdentityResult result = sts.getCallerIdentity(getCallerIdentityRequest); | ||
if (!result.getAccount().equals(targetAccount.trim().replace("-", ""))) { | ||
throw new RuntimeException(String.format("account: %s does not match target account: %s", result.getAccount(), targetAccount)); | ||
} | ||
} else { | ||
logger.info("No targetAccount was set; skipping AWS Account validation"); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
package com.intuit.cloudraider.model; | ||
|
||
import org.mockito.Mockito; | ||
|
||
import com.amazonaws.auth.AWSCredentialsProvider; | ||
import com.intuit.cloudraider.utils.AWSAccountValidator; | ||
|
||
import static org.junit.Assert.assertEquals; | ||
import static org.junit.Assert.assertSame; | ||
import static org.junit.Assert.fail; | ||
import static org.mockito.Mockito.*; | ||
|
||
import org.junit.Test; | ||
|
||
public class BasicCredentialsTest { | ||
|
||
@Test | ||
public void testValidateAccountNull() { | ||
System.setProperty("aws.accessKeyId", "accessKey"); | ||
System.setProperty("aws.secretKey", "secret"); | ||
AWSAccountValidator mockValidator = Mockito.mock(AWSAccountValidator.class); | ||
BasicCredentials creds = new BasicCredentials(mockValidator); | ||
|
||
creds.getAwsCredentials(); | ||
|
||
verify(mockValidator).validateAccount(null); | ||
|
||
} | ||
|
||
@Test | ||
public void testValidateAccount() { | ||
System.setProperty("aws.accessKeyId", "accessKey"); | ||
System.setProperty("aws.secretKey", "secret"); | ||
AWSAccountValidator mockValidator = Mockito.mock(AWSAccountValidator.class); | ||
BasicCredentials creds = new BasicCredentials(mockValidator); | ||
creds.setTargetAccount("1234"); | ||
|
||
creds.getAwsCredentials(); | ||
|
||
verify(mockValidator).validateAccount("1234"); | ||
|
||
} | ||
|
||
|
||
@Test | ||
public void testValidateAccountInvalid() { | ||
AWSAccountValidator mockValidator = Mockito.mock(AWSAccountValidator.class); | ||
BasicCredentials creds = new BasicCredentials(mockValidator); | ||
creds.setTargetAccount("1234"); | ||
RuntimeException expected = new RuntimeException(); | ||
|
||
doThrow(expected).when(mockValidator).validateAccount("1234"); | ||
|
||
try { | ||
creds.getAwsCredentials(); | ||
fail("getAwsCredentials did not throw an exception"); | ||
} catch(RuntimeException e) { | ||
assertSame(expected, e); | ||
} | ||
|
||
|
||
} | ||
|
||
@Test | ||
public void testSetTargetAccountViaSystemProperties() { | ||
System.setProperty("aws.targetAccount", "76543"); | ||
BasicCredentials creds = new BasicCredentials(); | ||
assertEquals("76543", creds.getTargetAccount()); | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
package com.intuit.cloudraider.utils; | ||
|
||
import org.mockito.Mockito; | ||
import org.mockito.invocation.InvocationOnMock; | ||
import org.mockito.stubbing.Answer; | ||
|
||
import com.amazonaws.auth.AWSCredentialsProvider; | ||
import com.amazonaws.services.securitytoken.AWSSecurityTokenService; | ||
import com.amazonaws.services.securitytoken.model.GetCallerIdentityRequest; | ||
import com.amazonaws.services.securitytoken.model.GetCallerIdentityResult; | ||
|
||
import static org.junit.Assert.assertEquals; | ||
import static org.junit.Assert.assertNotNull; | ||
import static org.junit.Assert.fail; | ||
import static org.mockito.Mockito.*; | ||
|
||
import org.junit.Test; | ||
|
||
public class AWSAccountValidatorTest { | ||
|
||
@Test | ||
public void validateAccountNullTest() { | ||
|
||
AWSCredentialsProvider mockProvider = Mockito.mock(AWSCredentialsProvider.class); | ||
AWSAccountValidator validator = new AWSAccountValidator(mockProvider); | ||
|
||
AWSSecurityTokenService mockSts = Mockito.mock(AWSSecurityTokenService.class); | ||
|
||
assertNotNull(validator.getSts()); | ||
|
||
validator.setSts(mockSts); | ||
|
||
validator.validateAccount(null); | ||
|
||
verifyZeroInteractions(mockSts); | ||
|
||
} | ||
|
||
@Test | ||
public void validateAccountNotValid() { | ||
|
||
AWSCredentialsProvider mockProvider = Mockito.mock(AWSCredentialsProvider.class); | ||
AWSAccountValidator validator = new AWSAccountValidator(mockProvider); | ||
|
||
AWSSecurityTokenService mockSts = Mockito.mock(AWSSecurityTokenService.class); | ||
|
||
assertNotNull(validator.getSts()); | ||
|
||
validator.setSts(mockSts); | ||
|
||
when(mockSts.getCallerIdentity((GetCallerIdentityRequest) notNull())).thenAnswer(new Answer<GetCallerIdentityResult>() { | ||
|
||
@Override | ||
public GetCallerIdentityResult answer(InvocationOnMock invocation) throws Throwable { | ||
GetCallerIdentityResult result = new GetCallerIdentityResult(); | ||
result.setAccount("5678"); | ||
return result; | ||
} | ||
|
||
}); | ||
|
||
try { | ||
validator.validateAccount("1234"); | ||
fail("validateAccount did not throw an exception"); | ||
} catch (RuntimeException e) { | ||
assertEquals("account: 5678 does not match target account: 1234", e.getMessage()); | ||
} | ||
} | ||
|
||
@Test | ||
public void validateAccountValid() { | ||
|
||
AWSCredentialsProvider mockProvider = Mockito.mock(AWSCredentialsProvider.class); | ||
AWSAccountValidator validator = new AWSAccountValidator(mockProvider); | ||
|
||
AWSSecurityTokenService mockSts = Mockito.mock(AWSSecurityTokenService.class); | ||
|
||
assertNotNull(validator.getSts()); | ||
|
||
validator.setSts(mockSts); | ||
|
||
when(mockSts.getCallerIdentity((GetCallerIdentityRequest) notNull())).thenAnswer(new Answer<GetCallerIdentityResult>() { | ||
|
||
@Override | ||
public GetCallerIdentityResult answer(InvocationOnMock invocation) throws Throwable { | ||
GetCallerIdentityResult result = new GetCallerIdentityResult(); | ||
result.setAccount("1234"); | ||
return result; | ||
} | ||
|
||
}); | ||
|
||
validator.validateAccount("1234"); | ||
} | ||
|
||
@Test | ||
public void validateAccountValidWithDashes() { | ||
|
||
AWSCredentialsProvider mockProvider = Mockito.mock(AWSCredentialsProvider.class); | ||
AWSAccountValidator validator = new AWSAccountValidator(mockProvider); | ||
|
||
AWSSecurityTokenService mockSts = Mockito.mock(AWSSecurityTokenService.class); | ||
|
||
assertNotNull(validator.getSts()); | ||
|
||
validator.setSts(mockSts); | ||
|
||
when(mockSts.getCallerIdentity((GetCallerIdentityRequest) notNull())).thenAnswer(new Answer<GetCallerIdentityResult>() { | ||
|
||
@Override | ||
public GetCallerIdentityResult answer(InvocationOnMock invocation) throws Throwable { | ||
GetCallerIdentityResult result = new GetCallerIdentityResult(); | ||
result.setAccount("1234"); | ||
return result; | ||
} | ||
|
||
}); | ||
|
||
validator.validateAccount("12-3-4-"); | ||
} | ||
|
||
@Test | ||
public void validateAccountValidWithWhitespace() { | ||
|
||
AWSCredentialsProvider mockProvider = Mockito.mock(AWSCredentialsProvider.class); | ||
AWSAccountValidator validator = new AWSAccountValidator(mockProvider); | ||
|
||
AWSSecurityTokenService mockSts = Mockito.mock(AWSSecurityTokenService.class); | ||
|
||
assertNotNull(validator.getSts()); | ||
|
||
validator.setSts(mockSts); | ||
|
||
when(mockSts.getCallerIdentity((GetCallerIdentityRequest) notNull())).thenAnswer(new Answer<GetCallerIdentityResult>() { | ||
|
||
@Override | ||
public GetCallerIdentityResult answer(InvocationOnMock invocation) throws Throwable { | ||
GetCallerIdentityResult result = new GetCallerIdentityResult(); | ||
result.setAccount("1234"); | ||
return result; | ||
} | ||
|
||
}); | ||
|
||
validator.validateAccount("12-3-4- "); | ||
} | ||
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is a new constructor which will not be invoked by default... would recommend adding this logic in the default constructor so that all existing usages get this feature
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added this so that I could inject a mock instance in order to test with. The logic in getCredentials will create an instance of AWSAccountValidator if one does not exist so existing usages will get this feature