Skip to content

Commit

Permalink
add client attestation mgt
Browse files Browse the repository at this point in the history
  • Loading branch information
Thumimku committed Nov 3, 2023
1 parent efd4c35 commit 9a3edb4
Show file tree
Hide file tree
Showing 15 changed files with 241 additions and 50 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,7 @@ public class ServiceProvider implements Serializable {

private static final String IS_B2B_SELF_SERVICE_APP = "IsB2BSelfServiceApp";
private static final String ASSOCIATED_ROLES_CONFIG = "AssociatedRolesConfig";
private static final String IS_API_BASED_AUTHENTICATION_ENABLED = "IsAPIBasedAuthenticationEnabled";
private static final String IS_ATTESTATION_ENABLED = "IsAttestationEnabled";
private static final String ANDROID_PACKAGE_NAME = "AndroidPackageName";
private static final String ANDROID_ATTESTATION_SERVICE_CREDENTIALS = "AndroidAttestationServiceCredentials";

private static final String IS_API_BASED_AUTHENTICATION_ENABLED = "IsAPIBasedAuthenticationEnabled";

@XmlTransient
@JsonIgnore
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,6 @@ private IdentityApplicationConstants() {
public static final String APPLICATION_SECRET_TYPE_ANDROID_ATTESTATION_CREDENTIALS
= "ANDROID_ATTESTATION_CREDENTIALS";


/**
* Config elements.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,6 @@ protected void activate(ComponentContext context) {
bundleContext.registerService(ApplicationMgtListener.class,
new AdminRolePermissionsUpdateListener(), null);

// SecretResolveManager secretManager = new SecretResolveManagerImpl();
// ApplicationManagementServiceComponentHolder.getInstance().setSecretResolveManager(secretManager);
if (log.isDebugEnabled()) {
log.debug("Identity ApplicationManagementComponent bundle is activated");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ public class ApplicationManagementServiceComponentHolder {
private SecretManager secretManager;
private SecretResolveManager secretResolveManager;


private ApplicationManagementServiceComponentHolder() {

}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com).
~
~ WSO2 LLC. licenses this file to you 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.
~
-->

<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

<parent>
<groupId>org.wso2.carbon.identity.framework</groupId>
<artifactId>client-attestation-mgt</artifactId>
<version>5.25.399</version>
<version>5.25.492-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down Expand Up @@ -150,4 +169,4 @@
</plugin>
</plugins>
</build>
</project>
</project>
Original file line number Diff line number Diff line change
Expand Up @@ -40,19 +40,15 @@
* The `ClientAttestationServiceImpl` class implements the `ClientAttestationService` interface and is responsible for
* validating client attestation. It ensures the authenticity and context of the client when
* API-based authentication is requested.
*
* The class provides the following functionalities:
*
* - Validation of attestation data, which can be specific to an Android client.
* - Checks whether API-based authentication is enabled for the client application.
* - Determines whether the client application is subscribed to client attestation validation.
* - Validates attestation objects provided by the client application.
* - Retrieves the service provider's configuration for OAuth2 client authentication.
*
* - Retrieves the service provider's configuration for client attestation.
* Usage:
* To validate client attestation, use the `validateAttestation` method, which takes the attestation
* object, client ID, and tenant domain as parameters.
*
* Example usage:
* ```
* ClientAttestationService clientAttestationService = new ClientAttestationServiceImpl();
Expand Down Expand Up @@ -85,8 +81,8 @@ public ClientAttestationContext validateAttestation(String attestationObject,
" is not subscribed to API-Based Authentication.");
}
clientAttestationContext.setApiBasedAuthenticationEnabled(false);
clientAttestationContext.setAttested(false);
clientAttestationContext.setAttestationEnabled(false);
clientAttestationContext.setAttested(false);
clientAttestationContext.setErrorMessage("App is not subscribed to API-Based Authentication.");
return clientAttestationContext;
}
Expand Down Expand Up @@ -114,9 +110,9 @@ public ClientAttestationContext validateAttestation(String attestationObject,
LOG.debug("App :" + serviceProvider.getApplicationResourceId() + " in tenant : " + tenantDomain +
" is requested with empty attestation object.");
}
clientAttestationContext.setApiBasedAuthenticationEnabled(false);
clientAttestationContext.setApiBasedAuthenticationEnabled(true);
clientAttestationContext.setAttestationEnabled(true);
clientAttestationContext.setAttested(false);
clientAttestationContext.setAttestationEnabled(false);
clientAttestationContext.setErrorMessage("App is configured to validate attestation " +
"but attestation object is empty.");
return clientAttestationContext;
Expand All @@ -131,18 +127,31 @@ public ClientAttestationContext validateAttestation(String attestationObject,
ClientAttestationValidator androidAttestationValidator = new AndroidAttestationValidator(clientId,
tenantDomain, serviceProvider.getClientAttestationMetaData());
androidAttestationValidator.validateAttestation(attestationObject, clientAttestationContext);
return clientAttestationContext;
} else {
handleInvalidAttestationObject(clientAttestationContext);
return clientAttestationContext;
}
} catch (ClientAttestationMgtException e) {
handleClientAttestationException(e, clientAttestationContext);
return clientAttestationContext;
}
}

private void handleInvalidAttestationObject(ClientAttestationContext clientAttestationContext) {

if (LOG.isDebugEnabled()) {
LOG.debug("Requested attestation object is not in valid format.");
}
return clientAttestationContext;
setErrorToContext("Requested attestation object is not in valid format.",
clientAttestationContext);
}

private void handleClientAttestationException
(ClientAttestationMgtException e, ClientAttestationContext clientAttestationContext) {

if (LOG.isDebugEnabled()) {
LOG.debug("Error while evaluating client attestation. ", e);
LOG.debug("Error while evaluating client attestation.", e);
}
setErrorToContext(e.getMessage(), clientAttestationContext);
}
Expand All @@ -156,15 +165,16 @@ private void setErrorToContext(String message, ClientAttestationContext clientAt
clientAttestationContext.setErrorMessage(message);
}

private boolean isAndroidAttestation(String attestationObject) throws ClientAttestationMgtException {
private boolean isAndroidAttestation(String attestationObject) {

try {
JWEObject jweObject = JWEObject.parse(attestationObject);

// Check if the JWEObject is in a valid state
return jweObject.getState() == JWEObject.State.ENCRYPTED;
} catch (ParseException e) {
throw new ClientAttestationMgtException("Exception occurred when parsing attestation object.", e);
// Exception occurred hence it's not a android attestation request.
return false;
}
}

Expand All @@ -185,7 +195,7 @@ private ServiceProvider getServiceProvider(String clientId, String tenantDomain)
LOG.debug("Could not find an application for client id: " + clientId
+ ", scope: " + OAUTH2 + ", tenant: " + tenantDomain);
}
throw new ClientAttestationMgtException("Service Provider not found");
throw new ClientAttestationMgtException("Service Provider not found.");
}
if (LOG.isDebugEnabled()) {
LOG.debug("Retrieved service provider: " + serviceProvider.getApplicationName() + " for client: " +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,17 +54,13 @@
* The `AndroidAttestationValidator` class is responsible for validating client attestation for Android clients.
* It ensures the authenticity and integrity of the client's attestation data, which is typically provided in the
* form of an integrity token.
*
* The class provides the following functionalities:
*
* - Decoding and verifying the authenticity of the provided integrity token using the Google Play Integrity API.
* - Validating the overall integrity of the client's request, including request details and application integrity.
* - Checking if the application is recognized as "PLAY_RECOGNIZED" by the Google Play Integrity API.
*
* Usage:
* To validate client attestation for Android clients, use the `validateAttestation` method, which takes the
* attestation header and a context to store validation results and updated information.
*
* Example usage:
* ```
* AndroidAttestationValidator attestationValidator = new AndroidAttestationValidator(clientId, tenantDomain, metaData);
Expand Down Expand Up @@ -118,10 +114,10 @@ public void validateAttestation(String attestationHeader, ClientAttestationConte
* @param attestationObject The integrity token to be decoded and verified.
* @param clientAttestationContext The context to store the validation results and updated information.
* @return The response containing the decoded integrity token data.
* @throws ClientAttestationMgtException Thrown when there is an
* issue with decoding or verifying the integrity token.
* @throws ClientAttestationMgtException Thrown when there is an issue with decoding or verifying the
* integrity token.
*/
public DecodeIntegrityTokenResponse decodeIntegrityToken(String attestationObject,
private DecodeIntegrityTokenResponse decodeIntegrityToken(String attestationObject,
ClientAttestationContext clientAttestationContext)
throws ClientAttestationMgtException {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@

package org.wso2.carbon.identity.client.attestation.mgt.validators;


import org.wso2.carbon.identity.client.attestation.mgt.exceptions.ClientAttestationMgtException;
import org.wso2.carbon.identity.client.attestation.mgt.model.ClientAttestationContext;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
/*
* Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com).
*
* WSO2 LLC. licenses this file to you 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 org.wso2.carbon.identity.client.attestation.mgt;

import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.testng.PowerMockTestCase;
import org.testng.Assert;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import org.wso2.carbon.identity.application.common.IdentityApplicationManagementException;
import org.wso2.carbon.identity.application.common.model.ClientAttestationMetaData;
import org.wso2.carbon.identity.application.common.model.ServiceProvider;
import org.wso2.carbon.identity.application.mgt.ApplicationManagementService;
import org.wso2.carbon.identity.client.attestation.mgt.internal.ClientAttestationMgtDataHolder;
import org.wso2.carbon.identity.client.attestation.mgt.model.ClientAttestationContext;
import org.wso2.carbon.identity.client.attestation.mgt.services.ClientAttestationService;
import org.wso2.carbon.identity.client.attestation.mgt.services.ClientAttestationServiceImpl;

import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.powermock.api.mockito.PowerMockito.mockStatic;
import static org.wso2.carbon.identity.client.attestation.mgt.utils.Constants.OAUTH2;

/**
* Testing the ClientAttestationServiceImpl class
*/
@PrepareForTest({ ClientAttestationServiceImpl.class, ClientAttestationMgtDataHolder.class,
ApplicationManagementService.class})
public class ClientAttestationServiceImplTest extends PowerMockTestCase {

private ClientAttestationService clientAttestationService;
private ApplicationManagementService applicationManagementService;

@BeforeMethod
public void setup() {

clientAttestationService = new ClientAttestationServiceImpl();

ClientAttestationMgtDataHolder clientAttestationMgtDataHolder = mock(ClientAttestationMgtDataHolder.class);
mockStatic(ClientAttestationMgtDataHolder.class);
applicationManagementService = mock(ApplicationManagementService.class);
mockStatic(ApplicationManagementService.class);
when(ClientAttestationMgtDataHolder.getInstance())
.thenReturn(clientAttestationMgtDataHolder);
when(ClientAttestationMgtDataHolder.getInstance().getApplicationManagementService())
.thenReturn(applicationManagementService);
}

@DataProvider(name = "validateAttestationDataProvider")
public Object[][] dataValidateAttestationMethod() {

return new Object[][]{
{
"", "tyHuiopdtQdlriM4df5mqCJZEa", "carbon.super", false, false, false,
"App is not subscribed to API-Based Authentication."
},
{
"", "tyHuiopdtQdlriM4df5mqCJZEa", "carbon.super", true, false, true,
null
},
{
"", "tyHuiopdtQdlriM4df5mqCJZEa", "carbon.super", true, true, false,
"App is configured to validate attestation but attestation object is empty."
},
{
"DUMMY OBJECT", "tyHuiopdtQdlriM4df5mqCJZEa", "carbon.super", true, true, false,
"Requested attestation object is not in valid format."
}
};
}

@Test(dataProvider = "validateAttestationDataProvider")
public void validateAttestationTest(String attestationObject,
String clientId,
String tenantDomain,
boolean isAPIBasedAuthenticationEnabled,
boolean isAttestationEnabled,
boolean isAttested,
String errorMessage) throws IdentityApplicationManagementException {

ServiceProvider testSp1 = new ServiceProvider();
testSp1.setAPIBasedAuthenticationEnabled(isAPIBasedAuthenticationEnabled);
ClientAttestationMetaData clientAttestationMetaData = new ClientAttestationMetaData();
clientAttestationMetaData.setAttestationEnabled(isAttestationEnabled);
testSp1.setClientAttestationMetaData(clientAttestationMetaData);

when(applicationManagementService.getServiceProviderByClientId(anyString(), eq(OAUTH2), anyString()))
.thenReturn(testSp1);
ClientAttestationContext clientAttestationContext =
clientAttestationService.validateAttestation(attestationObject,
clientId, tenantDomain);
Assert.assertEquals(isAttested, clientAttestationContext.isAttested(),
"False Client Attestation Validation");
if (errorMessage != null) {
Assert.assertEquals(errorMessage, clientAttestationContext.getErrorMessage(),
"Wrong Error message.");
}
}

@Test
public void validateAttestationTestForNullSP() throws IdentityApplicationManagementException {

when(applicationManagementService.getServiceProviderByClientId(anyString(), eq(OAUTH2), anyString()))
.thenReturn(null);
ClientAttestationContext clientAttestationContext =
clientAttestationService.validateAttestation(" ",
"tyHuiopdtQdlriM4df5mqCJZEa", "carbon.super");
Assert.assertFalse(clientAttestationContext.isAttested(),
"Client Attestation Validation should be false for null SP.");
Assert.assertEquals(clientAttestationContext.getErrorMessage(),
"Service Provider not found.");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<!--
~ Copyright (c) 2019, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
~
~ 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.
-->

<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >

<suite name="org.wso2.carbon.identity.client.attestation.mgt.test">
<test name="org.wso2.carbon.identity.client.attestation.mgt.test" preserve-order="false" parallel="false">
<classes>
<class name="org.wso2.carbon.identity.client.attestation.mgt.ClientAttestationServiceImplTest"/>
</classes>
</test>
</suite>
Loading

0 comments on commit 9a3edb4

Please sign in to comment.