Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Contexto
Para tratar de abordar esta situación, puede ser interesante automatizar la
evaluación de la definición e implementación de autorizaciones en la
aplicación. Esto, para garantizar constantemente que la implementación de las
autorizaciones en la aplicación sea coherente con la definición de
autorizaciones.
Objetivo
Este artículo describe una propuesta de implementación para automatizar las
pruebas de una matriz de autorización .
Roles de nodo : este nodo describe los posibles roles lógicos utilizados en
el sistema, se utiliza para proporcionar una lista y la explicación de los
diferentes roles (nivel de autorización).
Servicios de nodo : este nodo enumera y describe los servicios
disponibles expuestos por el sistema y los roles lógicos asociados que
pueden llamarlos.
Prueba de servicios de nodo : este nodo proporciona una carga útil de
prueba para cada servicio si el servicio utiliza datos de entrada distintos de
los que provienen de url o ruta.
<!-- Describe the possible logical roles used in the system, is used
here to
provide a list+explanation
of the different roles (authorization level) -->
<roles>
<role name="ANONYMOUS"
description="Indicate that no authorization is needed"/>
<role name="BASIC"
description="Role affected to a standard user (lowest access right
just above anonymous)"/>
<role name="ADMIN"
description="Role affected to a administrator user (highest access
right)"/>
</roles>
<!-- List and describe the available services exposed by the system
and the associated
logical role(s) that can call them -->
<services>
<service name="ReadSingleMessage" uri="/{messageId}" http-
method="GET"
http-response-code-for-access-allowed="200" http-response-code-
for-access-denied="403">
<role name="ANONYMOUS"/>
<role name="BASIC"/>
<role name="ADMIN"/>
</service>
<service name="ReadAllMessages" uri="/" http-method="GET"
http-response-code-for-access-allowed="200" http-response-code-
for-access-denied="403">
<role name="ANONYMOUS"/>
<role name="BASIC"/>
<role name="ADMIN"/>
</service>
<service name="CreateMessage" uri="/" http-method="PUT"
http-response-code-for-access-allowed="200" http-response-code-
for-access-denied="403">
<role name="BASIC"/>
<role name="ADMIN"/>
</service>
<service name="DeleteMessage" uri="/{messageId}" http-
method="DELETE"
http-response-code-for-access-allowed="200" http-response-code-
for-access-denied="403">
<role name="ADMIN"/>
</service>
</services>
</authorization-matrix>
Pruebas de integración
Las pruebas de integración se implementan utilizando un máximo de código
factorizado y se ha creado un caso de prueba por Punto de vista (POV) para
agrupar las verificaciones por perfil de nivel de acceso (rol lógico) y facilitar la
representación / identificación de los errores.
/**
* Integration Test cases in charge of validate the correct implementation
of the authorization matrix.
* Create on test case by logical role that will test access on all services
exposed by the system.
* Implements here focus on readability
*/
public class AuthorizationMatrixIT {
/**
* Object representation of the authorization matrix
*/
private static AuthorizationMatrix AUTHZ_MATRIX;
/**
* Load the authorization matrix in objects tree
*
* @throws Exception If any error occurs
*/
@BeforeClass
public static void globalInit() throws Exception {
try (FileInputStream fis = new FileInputStream(new
File("authorization-matrix.xml"))) {
SAXParserFactory spf = SAXParserFactory.newInstance();
spf.setFeature("http://xml.org/sax/features/external-general-
entities", false);
spf.setFeature("http://xml.org/sax/features/external-
parameter-entities", false);
spf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-
dtd", false);
Source xmlSource = new
SAXSource(spf.newSAXParser().getXMLReader(), new InputSource(fis));
JAXBContext jc =
JAXBContext.newInstance(AuthorizationMatrix.class);
AUTHZ_MATRIX = (AuthorizationMatrix)
jc.createUnmarshaller().unmarshal(xmlSource);
}
}
/**
* Test access to the services from a anonymous user.
*
* @throws Exception
*/
@Test
public void testAccessUsingAnonymousUserPointOfView() throws Exception
{
//Run the tests - No access token here
List<String> errors =
executeTestWithPointOfView(SecurityRole.ANONYMOUS, null);
//Verify the test results
Assert.assertEquals("Access issues detected using the ANONYMOUS
USER point of view:\n" + formatErrorsList(errors), 0, errors.size());
}
/**
* Test access to the services from a basic user.
*
* @throws Exception
*/
@Test
public void testAccessUsingBasicUserPointOfView() throws Exception {
//Get access token representing the authorization for the
associated point of view
String accessToken = generateTestCaseAccessToken("basic",
SecurityRole.BASIC);
//Run the tests
List<String> errors =
executeTestWithPointOfView(SecurityRole.BASIC, accessToken);
//Verify the test results
Assert.assertEquals("Access issues detected using the BASIC USER
point of view:\n " + formatErrorsList(errors), 0, errors.size());
}
/**
* Test access to the services from a administrator user.
*
* @throws Exception
*/
@Test
public void testAccessUsingAdministratorUserPointOfView() throws
Exception {
//Get access token representing the authorization for the
associated point of view
String accessToken = generateTestCaseAccessToken("admin",
SecurityRole.ADMIN);
//Run the tests
List<String> errors =
executeTestWithPointOfView(SecurityRole.ADMIN, accessToken);
//Verify the test results
Assert.assertEquals("Access issues detected using the ADMIN USER
point of view:\n" + formatErrorsList(errors), 0, errors.size());
}
/**
* Evaluate the access to all service using the point of view (POV)
specified.
*
* @param pointOfView Point of view to use
* @param accessToken Access token that is linked to the point of view
in terms of authorization.
* @return List of errors detected
* @throws Exception If any error occurs
*/
private List<String> executeTestWithPointOfView(SecurityRole
pointOfView, String accessToken) throws Exception {
List<String> errors = new ArrayList<>();
String errorMessageTplForUnexpectedReturnCode = "The service '%s'
when called with POV '%s' return a response code %s that is not the expected
one in allowed or denied case.";
String errorMessageTplForIncorrectReturnCode = "The service '%s'
when called with POV '%s' return a response code %s that is not the expected
one (%s expected).";
String fatalErrorMessageTpl = "The service '%s' when called with
POV %s meet the error: %s";
errors.add(String.format(errorMessageTplForIncorrectReturnCode,
service.getName(), pointOfView.name(), serviceResponseCode,
service.getHttpResponseCodeForAccessDenied()));
}
} else if (serviceResponseCode ==
service.getHttpResponseCodeForAccessDenied()) {
//Roles is in the list of role allowed to access to
the service so it's an error
if (accessIsGrantedInAuthorizationMatrix) {
errors.add(String.format(errorMessageTplForIncorrectReturnCode,
service.getName(), pointOfView.name(), serviceResponseCode,
service.getHttpResponseCodeForAccessAllowed()));
}
} else {
errors.add(String.format(errorMessageTplForUnexpectedReturnCode,
service.getName(), pointOfView.name(), serviceResponseCode));
}
} catch (Exception e) {
errors.add(String.format(fatalErrorMessageTpl,
service.getName(), pointOfView.name(), e.getMessage()));
}
});
return errors;
}
/**
* Call a service with a specific payload and return the HTTP response
code received.
* Delegate this step in order to made the test cases more easy to
maintain.
*
* @param uri URI of the target service
* @param payloadContentType Content type of the payload to send
* @param payload Payload to send
* @param httpMethod HTTP method to use
* @param accessToken Access token to specify to represent the
identity of the caller
* @return The HTTP response code received
* @throws Exception If any error occurs
*/
private int callService(String uri, String payload, String
payloadContentType, String httpMethod, String accessToken) throws Exception
{
int rc;
return rc;
}
/**
* Generate a JWT token the user and role specified.
*
* @param login User login
* @param role Authorization logical role
* @return The JWT token
* @throws Exception If any error occurs during the creation
*/
private String generateTestCaseAccessToken(String login, SecurityRole
role) throws Exception {
return new AuthService().issueAccessToken(login, role);
}
/**
* Format a list of errors to a printable string
*
* @param errors Error list
* @return Printable string
*/
private String formatErrorsList(List<String> errors) {
StringBuilder buffer = new StringBuilder();
errors.forEach(e -> buffer.append(e).append("\n"));
return buffer.toString();
}
}
En caso de detección de un problema de autorización, el resultado es el
siguiente:
testAccessUsingAnonymousUserPointOfView(org.owasp.pocauthztesting.Authoriza
tionMatrixIT)
Time elapsed: 1.009 s ### FAILURE
java.lang.AssertionError:
Access issues detected using the ANONYMOUS USER point of view:
The service 'DeleteMessage' when called with POV 'ANONYMOUS' return
a response code 200 that is not the expected one (403 expected).
The service 'CreateMessage' when called with POV 'ANONYMOUS' return
a response code 200 that is not the expected one (403 expected).
testAccessUsingBasicUserPointOfView(org.owasp.pocauthztesting.Authorization
MatrixIT)
Time elapsed: 0.05 s ### FAILURE!
java.lang.AssertionError:
Access issues detected using the BASIC USER point of view:
The service 'DeleteMessage' when called with POV 'BASIC' return
a response code 200 that is not the expected one (403 expected).
Representación de la matriz de
autorización para auditoría / revisión.
Incluso si la matriz de autorización se almacena en un formato legible por
humanos (XML), puede ser interesante proporcionar una representación de
representación sobre la marcha del archivo XML para facilitar la revisión,
auditoría y discusión sobre la matriz de autorización para para detectar
posibles inconsistencias.