This is a single assignment that has been broken into incremental, compatible portions based on the completed API assignment. It should be turned in as a single tree of Maven modules that can build from the root level down.

1. Assignment Starter

There is a project within homesales-starter/assignment3-homesales-security/homesales-security-svc that contains some ground work for the security portions of the assignment. It contains:

  1. a set of @Configuration classes nested within the SecurityConfiguration class. Each @Configuration class or construct is profile-constrained to match a section of the assignment.

  2. the shell of a secure HomeSalesService wrapper

    It is your choice whether to use the "layered/wrapped" approach (where you implement separate/layered API and security modules) or "embedded/enhanced" approach (where you simply enhance the API solution with the security requirements).
  3. a @Configuration class that instantiates the correct HomeSaleService under the given profile/runtime context.

  4. base unit integration tests that align with the sections of the assignment and pull in base tests from the support module. Each test case activates one or more profiles identified by the assignment.

assignment3c homesales starter
Figure 1. Security Assignment Starter

The meat of the assignment is focused in the following areas

  1. configuring web security beans to meet the different security levels of the assignment. You may use the deprecated WebSecurityConfigurerAdapter or its component-based replacement. Each profile will start over. The starter is configured for the new component-based approach. You will end up copy/pasting filtering rules forward to follow-on configurations of advancing profiles.

  2. the unit tests are well populated

    1. each of the tests have been @Disabled and rely on your test helper from the API tests to map HomeSaleDTO references to your HomeSale DTO class.

  3. For the other server-side portions of the assignment

    1. there is a skeletal IdentityController that lays out portions of the whoAmI and authorities endpoints.

    2. there is a skeletal SecureHomeSalesServiceWrapper and @Configuration class that can be used to optionally wrap your assignment 2 implementation. The satisfactory alternative is to populate your existing assignment 2 implementations to meet the assignment 3 requirements. Neither path (layer versus enhance in-place) will get you more or less credit. Choose the path that makes the most sense to you.

      The layered approach provides an excuse to separate the solution into layers of components and provide practice doing so.
      If you layer your assignment3 over your assignment2 solution as separate modules, you will need to make sure that the dependencies on assignment2 are vanilla JARs and not Spring Boot executable JARs. Luckily the ejava-build-parent made that easy to do with classifying the executable JAR with bootexec.

2. Assignment Support

The assignment support module(s) in homebuyers-support/homebuyers-support-security again provide some examples and ground work for you to complete the assignment — by adding a dependency. I used a layered approach to secure Homes and Buyers. This better highlighted what was needed for security because it removed most of the noise from the assignment 2 functional threads. It also demonstrated some weaving of components within the auto-configuration. Adding the dependency on the homebuyers-support-security-svc adds this layer to the homebuyers-support-api-svc components.

The following dependency can replace your current dependency on the homebuyers-support-api-svc.

homebuyers-support-security-svc Dependency
<dependency>
    <groupId>info.ejava.assignments.security.homesales</groupId>
    <artifactId>homebuyers-support-security-svc</artifactId>
    <version>${ejava.version}</version>
</dependency>

The support module comes with an extensive amount of tests that permit you to focus your attention on the security configuration and security implementation of the HomeSale service. The following test dependency can provide you with many test constructs.

homebuyers-support-security-svc Dependency
<dependency>
    <groupId>info.ejava.assignments.security.homesales</groupId>
    <artifactId>homebuyers-support-security-svc</artifactId>
    <classifier>tests</classifier>
    <version>${ejava.version}</version>
    <scope>test</scope>
</dependency>

2.1. High Level View

Between the support (primary and test) modules and starter examples, most of your focus can be placed on completing the security configuration and service implementation to satisfy the security requirements.

The support module provides

  • Home and Buyer services that will operate within your application and will be secured by your security configuration. Necessary internal security checks are included within the Home and Buyer services, but your security configuration will use path-based security access to provide interface access control to these externally provided resources.

The support test modules provide

  • @TestConfiguration classes that supply the necessary beans for the tests to be completed.

  • Test cases that are written to be base classes of @SpringBootTest test cases supplied in your assignment. The starter provides most of what you will need for your security tests.

assignment3c homesales highlevel
Figure 2. Security Assignment High Level View

Your main focus should be within the security configuration and HomeSale service implementation classes and running the series of provided tests.

The individual sections of this assignment are associated with one or more Spring profiles. The profiles are activated by your @SpringBootTest test cases. The profiles will activate certain test configurations and security configurations your are constructing.

  • Run a test

  • Fix a test result with either a security configuration or service change

  • rinse and repeat

The tests are written to execute from the sub-class in your area. With adhoc navigation, sometimes the IDE can get lost — lose the context of the sub-class and provide errors as if there were only the base class. If that occurs — make a more direct IDE command to run the sub-class to clear the issue.

3. Assignment 3a: Security Authentication

3.1. Anonymous Access

3.1.1. Purpose

In this portion of the assignment, you will demonstrate your knowledge of configuring Spring Web Security for authentication requirements. You will:

  1. activate Spring Security

  2. create multiple, custom authentication filter chains

  3. enable open access to static resources

  4. enable anonymous access to certain URIs

  5. enforce authenticated access to certain URIs

3.1.2. Overview

In this portion of the assignment you will be activating and configuring the security configuration to require authentication to certain resource operations while enabling access to other resources operations.

assignment3a homesales security authn anon
Figure 3. Anonymous Access

3.1.3. Requirements

  1. Add a static text file past_transactions.txt that will be made available below the /content/ URI. Place the following title in the first line so that the following will be made available from a web client.

    past_transactions.txt
    $ curl -X GET http://localhost:8080/content/past_transactions.txt
    Past HomeSales
    Static Resources Served from classpath

    By default, static content is served out of the classpath from several named locations including classpath:/static/, classpath:/public/, classpath:/resources/, and classpath:/META-INF/resources/

  2. Configure anonymous access for the following resources methods

    1. static content below /content

      Configure HttpSecurity to ignore all calls below /content/. Leverage the antMatchers() to express the pattern.
      /content/** blob indicates "anything below" /content.
    2. HEAD for all resources

    3. GET for home and homeSale resources..

      Configure HttpSecurity to permit all HEAD calls matching any URI — whether it exists or not. Leverage the antMatcher() to express the method and pattern.
      Configure HttpSecurity to permit all GET calls matching URIs below homes and homeSales. Leverage the antMatcher() to express the method and pattern.
  3. Turn off CSRF protections.

    Configure HttpSecurity to disable CSRF processing. This will prevent ambiguity between a CSRF or authorization rejection for non-safe HTTP methods.
  4. Configure authenticated access for the following resource operations

    1. GET calls for buyer resources. No one can gain access to a Buyer without being authenticated.

    2. non-safe calls (POST, PUT, and DELETE) for homes, buyers, and homeSales resources

      Configure HttpSecurity to authenticate any request that was not yet explicitly permitted
  5. Create a unit integration test case that verifies (provided)

    1. anonymous user access granted to static content

    2. anonymous user access granted to a HEAD call to home and buyer resources

    3. anonymous user access granted to a GET call to home and homeSale resources

    4. anonymous user access denial to a GET call to buyer resources

    5. anonymous user access denial to non-safe call to each resource type

      Denial must be because of authentication requirements and not because of a CSRF failure. Disable CSRF for all API security configurations.
      All tests will use an anonymous caller in this portion of the assignment. Authenticated access is the focus of a later portion of the assignment.
  6. Restrict this security configuration to the anonymous-access profile and activate that profile while testing this section.

@Configuration(proxyBeanMethods = false)
@Profile("anonymous-access") (1)
public class PartA1_AnonymousAccess {
1 restrict configuration changes to the anonymous-access profile
@SpringBootTest(classes= {...},
@ActiveProfiles({"test", "anonymous-access"}) (1)
public class MyA1_AnonymousAccessNTest extends A1_AnonymousAccessNTest {
1 activate the anonymous-access profile (with any other designed profiles) when executing tests

3.1.4. Grading

Your solution will be evaluated on:

  1. activate Spring Security

    1. whether Spring security has been enabled

  2. create multiple, custom authentication filter chains

    1. whether access is granted or denied for different resource URIs and methods

  3. enable open access to static resources

    1. whether anonymous access is granted to static resources below /content

  4. enable anonymous access to certain URIs

    1. whether anonymous access has been granted to dynamic resources for safe (GET) calls

  5. enforce authenticated access to certain URIs

    1. whether anonymous access is denied for dynamic resources for unsafe (POST, PUT, and DELETE) calls

3.1.5. Additional Details

  1. No accounts are necessary for this portion of the assignment. All testing is performed using an anonymous caller.

3.2. Authenticated Access

3.2.1. Purpose

In this portion of the assignment, you will demonstrate your knowledge of authenticating a client and identifying the identity of a caller. You will:

  1. add an authenticated identity to RestTemplate or WebClient client

  2. locate the current authenticated user identity

3.2.2. Overview

In this portion of the assignment you will be authenticating with the API (using RestTemplate or WebClient) and tracking the caller’s identity within the application.

assignment3a homesales security authn auth
Figure 4. Authenticated Access

You’re starting point should be an application with functional Homes, Buyers, and HomeSales API services. Homes and Buyers were provided to you. You implemented HomeSales in assignment2. All functional tests for HomeSales are passing.

3.2.3. Requirements

  1. Configure the application to use a test username/password of ted/secret during testing with the authenticated-access profile.

    Account properties should only be active when using the authenticated-access profile.

    spring.security.user.name:
    spring.security.user.password:

    Active profiles can be named for individual Test Cases.

    @SpringBootTest(...)
    @ActiveProfiles({"test", "authenticated-access"})

    Property values can be injected into the test configurations in order to supply known values.

    @Value("${spring.security.user.name:}") (1)
    private String username;
    @Value("${spring.security.user.password:}")
    private String password;
    1 value injected into property if defined — blank if not defined
  2. Configure the application to support BASIC authentication.

    Configure HttpSecurity to enable HTTP BASIC authentication.
  3. Turn off CSRF protections.

    Configure HttpSecurity to disable CSRF processing.
  4. Turn off sessions, and any other filters that prevent API interaction beyond authentication.

    Configure HttpSecurity to use Stateless session management and other properties as required.
  5. Add a new resource api/whoAmI

    1. supply two access methods: GET and POST. Configure security such that neither require authentication.

      Configure HttpSecurity to permit all method requests for /api/whoAmI. No matter which HttpMethod is used. Pay attention to the order of the authorize requests rules definition.
    2. both methods must determine the identity of the current caller and return that value to the caller. When called with no authenticated identity, the methods should should return a String value of “(null)” (open_paren + null + close_paren)

      You may inject or programmatically lookup the user details for the caller identity within the server-side controller method.
  6. Create a set of unit integration tests that demonstrate the following (provided):

    1. authentication denial when using a known username but bad password

      Any attempt to authenticate with a bad credential will result in a 401/UNAUTHORIZED error no matter if the resource call requires authentication or not.
      Credentials can be applied to RestTemplate using interceptors.
    2. successful authentication using a valid username/password

    3. successful identification of the authenticated caller identity using the whoAmI resource operations

    4. successful authenticated access to POST/create home, buyer, and homeSale resource operations

  7. Restrict this security configuration to the authenticated-access profile and activate that profile during testing this section.

    @Configuration(proxyBeanMethods = false)
    @Profile({"authenticated-access", "userdetails"}) (1)
    public class PartA2_AuthenticatedAccess {
    ===
    @SpringBootTest(classes={...},
        webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
    @ActiveProfiles({"test", "authenticated-access"}) (2)
    public class MyA2_AuthenticatedAccessNTest extends A2_AuthenticatedAccessNTest {
    1 activated with authenticated-access profile
    2 activating desired profiles
  8. Establish a security configuration that is active for the nosecurity profile which allows for but does not require authentication to call each resource/method. This will be helpful to simplify some demonstration scenarios where security is not essential.

    @Configuration(proxyBeanMethods = false)
    @Profile("nosecurity") (1)
    public class PartA2b_NoSecurity {
    ===
    @SpringBootTest(classes= { ...
    @ActiveProfiles({"test", "nosecurity"}) (1)
    public class MyA2b_NoSecurityNTest extends A2b_NoSecurityNTest {

    The security profile will allow unauthenticated access to operations that are also free of CSRF checks.

    curl -X POST http://localhost:8080/api/homes -H 'Content-Type: application/json' -d '{ ... }'
    { ... }

3.2.4. Grading

Your solution will be evaluated on:

  1. add an authenticated identity to RestTemplate or WebClient client

    1. whether you have implemented stateless API authentication (BASIC) in the server

    2. whether you have successfully completed authentication using a Java client

    3. whether you have correctly demonstrated and tested for authentication denial

    4. whether you have demonstrated granted access to an unsafe methods for the home, buyer, and homeSale resources.

  2. locate the current authenticated user identity

    1. whether your server-side components are able to locate the identity of the current caller (authenticated or not).

3.3. User Details

3.3.1. Purpose

In this portion of the assignment, you will demonstrate your knowledge of assembling a UserDetailsService to track the credentials of all users is the service. You will:

  1. build a UserDetailsService implementation to host user accounts and be used as a source for authenticating users

  2. build an injectable UserDetailsService

  3. encode passwords

3.3.2. Overview

In this portion of the assignment, you will be starting with a security configuration with the authentication requirements of the previous section. The main difference here is that there will be multiple users and your server-side code needs to manage multiple accounts and permit them each to authenticate.

To accomplish this, you will be constructing an AuthenticationManager to provide the user credential authentication required by the policies in the SecurityFilterChain. Your supplied UserDetailService will be populated with at least 5 users when the application runs with the userdetails profile.

assignment3a homesales security authn cors
Figure 5. User Details

The homebuyers-support-security-svc module contains a YAML file activated with the userdetails profile. The YAML file expresses the following users with credentials. These are made available by injecting the Accounts @ConfigurationProperty bean.

  1. mary/secret

  2. lou/secret

  3. murry/secret

  4. ted/secret

  5. sueann/betty

3.3.3. Requirements

  1. Create a UserDetailsService that is activated during the userdetails profile.

    The InMemoryUserDetailsManager is fine for this requirement. You have the option of using other implementation types if you wish but they cannot require the presence of an external resource (i.e., JDBC option must use an in-memory database).
    1. expose the UserDetailsService as a @Bean that can be injected into other factories.

      @Bean
      public UserDetailsService userDetailsService(PasswordEncoder encoder, ...) {
    2. populate it with the 5 users from an injected Accounts @ConfigurationProperty bean

      The Accounts bean is provided for you in the ProvidedAuthorizationTestHelperConfiguration in the support module.
    3. store passwords for users using a BCrypt hash algorithm.

      Both the BCryptPasswordEncoder and the DelegatingPasswordEncoder will encrypt with Bcrypt.

  2. Create a unit integration test case that (provided):

    1. activates the userdetails profile

      @Configuration(proxyBeanMethods = false)
      @Profile({"nosecurity","userdetails", "authorities", "authorization"})(1)
      public class PartA3_UserDetailsPart {
      ===
      @SpringBootTest(classes={...},
      @ActiveProfiles({"test","userdetails"}) (2)
      public class MyA3_UserDetailsNTest extends A3_UserDetailsNTest {
      1 activated with userdetails profile
      2 activating desired profiles
    2. verifies successful authentication and identification of the authenticated username using the whoAmI resource

    3. verifies successful access to a one POST/create home, buyer, and homeSale resource operation for each user

      The test(s) within the support module provides much/all of this test coverage.

3.3.4. Grading

Your solution will be evaluated on:

  1. build a UserDetailsService implementation to host user accounts and be used as a source for authenticating users

    1. whether your solution can host credentials for multiple users

    2. whether your tests correctly identify the authenticated caller for each of the users

    3. whether your tests verify each authenticated user can create a Home, Buyer, and HomeSale

  2. build an injectable UserDetailsService

    1. whether the UserDetailsService was exposed using a @Bean factory

  3. encode passwords

    1. whether the password encoding was explicitly set to create BCrypt hash

3.3.5. Additional Details

  1. There is no explicit requirement that the UserDetailsService be implemented using a database. If you do use a database, use an in-memory RDBMS so that there are no external resources required.

  2. You may use a DelegatingPasswordEncoder to satisfy the BCrypt encoding requirements, but the value stored must be in BCrypt form.

  3. The implementation choice for PasswordEncoder and UserDetailsService is separate from one another and can be made in separate @Bean factories.

    @Bean
    public PasswordEncoder passwordEncoder() {...}
    @Bean
    public UserDetailsService userDetailsService(PasswordEncoder encoder, ...) {...}
  4. The commonality of tests differentiated by different account properties is made simpler with the use of JUnit @ParameterizedTest. However, by default method sources are required to be declared as Java static methods — unable to directly reference beans injected into non-static attributes. @TestInstance(TestInstance.Lifecycle.PER_CLASS) can be used to allow the method source to be declared a Java non-static method and directly reference the injected Spring context resources.

  5. You may annotate a @Test or @Nested testcase class with @DirtiesContext to indicate that the test makes changes that can impact other tests and the Spring context should be rebuilt after finishing.

4. Assignment 3b: Security Authorization

4.1. Authorities

4.1.1. Purpose

In this portion of the assignment, you will demonstrate your knowledge of authorities. You will:

  1. define role-based and permission-based authorities

4.1.2. Overview

In this portion of the assignment, you will start with the authorization and user details configuration of the previous section and enhance each user with authority information.

You will be assigning authorities to users and verifying them with a unit test. You will add an additional ("authorities") resource to help verify the assertions.

assignment3b homesales security authz whoami
Figure 6. Authorities Test Resource
assignment3b homesales security authz authts
Figure 7. Assignment Principles and Authorities

The homebuyers-support-security-svc module contains a YAML file activated with the authorities and authorization profiles. The YAML file expresses the following users, credentials, and authorities

  1. mary: ROLE_ADMIN, ROLE_MEMBER

  2. lou: ROLE_MGR (no role member)

  3. murry: ROLE_MEMBER, PROXY

  4. ted: ROLE_MEMBER

  5. sueann: ROLE_MEMBER

4.1.3. Requirements

  1. Create an api/authorities resource with a GET method

    1. accepts an "authority" query parameter

    2. returns (textual) "TRUE" or "FALSE" depending on whether the caller has the authority assigned

  2. Create one or more unit integration test cases that (provided):

    1. activates the authorities profile

    2. verifies the unauthenticated caller has no authorities assigned

    3. verifies each the authenticated callers have proper assigned authorities

4.1.4. Grading

Your solution will be evaluated on:

  1. define role-based and permission-based authorities

    1. whether you have assigned required authorities to users

    2. whether you have verified an unauthenticated caller does not have identified authorities assigned

    3. whether you have verified successful assignment of authorities for authenticated users as clients

4.1.5. Additional Details

  1. There is no explicit requirement to use a database for the user details in this assignment. However, if you do use an database, please use an in-memory RDBMS instance so there are no external resources required.

  2. The repeated tests due to different account data can be simplified using a @ParameterizedTest. However, you will need to make use of @TestInstance(TestInstance.Lifecycle.PER_CLASS) in order to leverage the Spring context in the @MethodSource.

4.2. Authorization

4.2.1. Purpose

In this portion of the assignment, you will demonstrate your knowledge of implementing access control based on authorities assigned for URI paths and methods. You will:

  1. implement URI path-based authorization constraints

  2. implement annotation-based authorization constraints

  3. implement role inheritance

  4. implement an AccessDeniedException controller advice to hide necessary stack trace information and provide useful error information to the caller

4.2.2. Overview

In this portion of the assignment you will be restricting access to specific resource operations based on path and expression-based resource restrictions.

assignment3b homesales security authz authzm
Figure 8. Authorization

4.2.3. Requirements

  1. Update the resources to store the identity of the caller with the resource created to identify the owner

    1. Homes will store the username (as username) of the home creator (provided)

    2. Buyers will store the username (as username) of the buyer creator (provided)

    3. HomeSales should store the username (as username) of the home it represents (new)

      The Home creator may create the HomeSale or a user with the PROXY permission (murry) may create the HomeSale for the creator of the Home.
    4. the identity should not be included in the marshalled DTO returned to callers

      The DTO may have this field omitted or marked transient so that the element is never included.
  2. Define access constraints for resources using path and expression-based authorizations. Specific authorization restriction details are in the next requirement.

    1. Use path-based authorizations for Home and Buyer resources, assigned to the URIs since you are not working with modifiable source code for these two resources

      Configure HttpSecurity to enforce the required roles for homes and buyers calls
    2. Use expression-based authorizations for homeSales resources, applied to the service methods

      Use annotations on HomeSale service methods to trigger authorization checks. Remember to enable global method security for the prePostEnabled annotation form for your application and tests.
  3. Restrict access according to the following

    1. Homes (path-based authorizations)

      1. continue to restrict non-safe methods to authenticated users (from Authenticated Access)

      2. authenticated users may modify homes that match their login (provided)

      3. authenticated users may delete homes that match their login or have the MGR role (provided)

      4. only authenticated users with the ADMIN role can delete all homes (new)

    2. Buyers (path-based authorizations)

      1. continue to restrict non-safe methods to authenticated users (from Authenticated Access)

      2. authenticated users may create a single Buyer to represent themselves (provided)

      3. authenticated users may modify a buyer that matches their login (provided)

      4. authenticated users may delete a buyer that matches their login or have the MGR role (provided)

      5. only authenticated users with the ADMIN role can delete all buyers (new)

    3. HomeSales (annotation-based authorizations) — through the use of @PreAuthorize and programmatic checks.

      1. authenticated users may create a HomeSale for an existing Home they created (new)

      2. authenticated users with the MEMBER role may update (purchase) a HomeSale with a Buyer they created (new)

        i.e. caller "sueann" can update (purchase) existing HomeSale (owned by ted) with Buyer representing "sueann"
      3. authenticated users with the MGR role or PROXY permission may update (purchase) a HomeSale with any Buyer (new)

        i.e. caller "lou" and "murry" can update (purchase) any existing HomeSale for any Buyer
      4. authenticated users may only delete a HomeSale for a Home matching their username (new)

      5. authenticated users with the MGR role may delete any HomeSale (new)

      6. authenticated users with the ADMIN role may delete all HomeSales (new)

  4. Form the following role inheritance

    1. ROLE_ADMIN inherits from ROLE_MGR so that users with ADMIN role will also be able to perform MGR role operations

      Register a RoleHierarchy relationship definition between inheriting roles.
  5. Implement a mechanism to hide stack trace or other details from the API caller response when an AccessDeniedException occurs. From this point forward — stack traces can be logged on the server-side but should not be exposed in the error payload.

  6. Create unit integration test(s) to demonstrate the behavior defined above

4.2.4. Grading

Your solution will be evaluated on:

  1. implement URI path-based authorization constraints

    1. whether path-based authorization constraints were properly defined and used for Home and Buyer resource URIs

  2. implement annotation-based authorization constraints

    1. whether expression-based authorization constraints were properly defined for HomeSale service methods

  3. implement role inheritance

    1. whether users with the ADMIN role were allowed to invoke methods constrained to the MGR role.

  4. implement an AccessDeniedException controller advice to hide necessary stack trace information and provide useful error information to the caller

    1. whether stack trace or other excessive information was hidden from the access denied caller response

4.2.5. Additional Details

  1. Role inheritance can be defined using a RoleHierarchy bean.

5. Assignment 3c: HTTPS

5.1. HTTPS

5.1.1. Purpose

In this portion of the assignment, you will demonstrate your knowledge of protecting sensitive data exchanges with one-way HTTPS encryption. You will:

  1. generate a self-signed certificate for demonstration use

  2. enable HTTPS/TLS within Spring Boot

  3. implement a Maven Failsafe integration test using HTTPS

5.1.2. Overview

In this portion of the assignment you will be configuring your server to support HTTPS only when the https profile is active.

assignment3b homesales security authz https
Figure 9. Authorization

5.1.3. Requirements

  1. Implement an HTTPS-only communication in the server when running with the https profile.

    1. package a demonstration certificate in the src tree

    2. supply necessary server-side properties in a properties file

      With this in place and the application started with the authorities, authorization, and https profiles active …​

      java -jar target/homesales-security-svc-1.0-SNAPSHOT-bootexec.jar --spring.profiles.active=authorities,authorization,https

      should enable the following results

      curl -k -X GET https://localhost:8443/api/whoAmI -u "mary:secret" && echo
      mary
      $ curl -k -X DELETE https://localhost:8443/api/homes -u mary:secret
      $ curl -k -X DELETE https://localhost:8443/api/homes -u sueann:betty
      {"timestamp":"2022-09-25T00:51:41.200+00:00","status":403,"error":"Forbidden","path":"/api/homes"}
  2. Implement an IT/Failsafe integration test that will

    1. start the server with the authorities, authorization, and https profiles active

      The starter module has the Maven pom.xml plugin basics for this.
    2. configure and start JUnit with an integration test

      The starter module has the Maven pom.xml plugin basics for this.
    3. establish an HTTPS connection with RestTemplate or WebClient

      The starter module provides a @TestConfiguration class that will instantiate a RestTemplate capable of using HTTPS and the test activates the "its" Spring profile. You need to supply the details of the HTTPS client properties within that (missing) properties file.
    4. successfully invoke using HTTPS and evaluate the result

      The starter module provides a skeletal test for you to complete. The actual test performed by you can be any end-to-end communication with the server that uses HTTPS.

5.1.4. Grading

Your solution will be evaluated on:

  1. generate a self-signed certificate for demonstration use

    1. whether a demonstration PKI certificate was supplied for demonstrating the HTTPS capability of the application

  2. enable HTTPS/TLS within Spring Boot

    1. whether the integration test client was able to perform round-trip communication with the server

    2. whether the communications used HTTPS protocol

5.1.5. Additional Details

  1. There is no requirement to implement an HTTP to HTTPS redirect

  2. See the svc/svc-security/https-hello-example for Maven and RestTemplate setup example.

  3. Implement the end-to-end integration test with HTTP before switching to HTTPS to limit making too many concurrent changes.

6. Assignment 3d: AOP and Method Proxies

In this assignment, we are going to use some cross-cutting aspects of Spring AOP and dynamic capabilities of Java reflection to modify component behavior. As a specific example, you are going to modify Home and Buyer service behavior without changing the Homes/Buyers source code. We are going to add a requirement that certain fields be null and non-null.

The first two sections (reflection and dynamic proxies) of the AOP assignment lead up to the final solution (aspects) in the third section.

No Homes/Buyers Compilation Dependencies
The src/main portions of the assignment must have no compilation dependency on Homes and Buyers. All compilation dependencies and most knowledge of Homes and Buyers will be in the JUnit tests.

6.1. Reflection

6.1.1. Purpose

In this portion of the assignment, you will demonstrate your knowledge of using reflection to obtain and invoke a method proxy. You will:

  1. obtain a method reference and invoke it using Java Reflection

6.1.2. Overview

In this portion of the assignment you will implement a set of helper methods for a base class (NullPropertyAssertion) located in the homebuyers-support-aop module — tasked with validating whether objects have nulls for an identified property. If the assertion fails — an exception will be thrown by the base class. Your derived class will assist in locating the method reference to the "getter" and invoking it to obtain the current property value.

assignment3c homesales reflection
Figure 10. AOP and Method Proxies

You will find an implementation class shell, @Configuration class with @Bean factory, and JUnit test in the assignment 3 security "starter".

No Home/Buyer Compilation Dependency
Note that you see no mention of Home or Buyer in the above description/diagram. Everything will be accomplished using Java reflection.

You will need to create a dependency on the Spring Boot AOP starter, the ejava AOP support JAR, and AOP test JAR.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
    <groupId>info.ejava.assignments.aop.homesales</groupId>
    <artifactId>homesales-support-aop</artifactId>
    <version>${ejava.version}</version>
</dependency>

<dependency>
    <groupId>info.ejava.assignments.aop.homesales</groupId>
    <artifactId>homesales-support-aop</artifactId>
    <classifier>tests</classifier>
    <version>${ejava.version}</version>
    <scope>test</scope>
</dependency>

6.1.3. Requirements

  1. implement the getGetterMethod() to locate and return the java.lang.reflect.Method for the requested (getter) method name.

    1. return the Method object if it exists

    2. use an Optional<Method> return type and return an Optional.empty() if it does not exist. It is not an error if the getter does not exist.

      It is not considered an error if the getterName requested does not exist. JUnit tests — which know the full context of the call — will decide if the result is correct or not.
  2. implement the getValue() method to return the value reported by invoking the getter method using reflection

    1. return the value returned

    2. report any exception thrown as a runtime exception

      Any exception calling an existing getter is unexpected and should be reported as a (subclass of) RuntimeException to the higher-level code to indicate this type of abnormality
  3. use the supplied JUnit unit tests to validate your solution. There is no Spring context required/used for this test.

    The tests will use non-HomeDTO/BuyerDTO objects on purpose. The validator under test must work with any type of object passed in. Only "getter" access will be supported for this capability. No "field" access will be performed.

6.1.4. Grading

Your solution will be evaluated on:

  1. obtain a method reference and invoke it using Java Reflection

    1. whether you are able to return a reference to the specified getter method using reflection

    2. whether you are able to obtain the current state of the property using the getter method reference using reflection

6.1.5. Additional Details

  1. The JUnit test (MyD1_ReflectionMethodTest) is supplied and should not need to be modified beyond enabling it.

  2. The tests will feed your solution with DTO instances in various valid and invalid state according to the isNull and notNull calls. There will also be some non-DTO classes used to verify the logic is suitable for generic parameter types.

6.2. Dynamic Proxies

6.2.1. Purpose

In this portion of the assignment, you will demonstrate your knowledge of implementing a dynamic proxy. You will:

  1. create a JDK Dynamic Proxy to implement adhoc interface(s) to form a proxy at runtime for implementing advice

6.2.2. Overview

In this portion of the assignment you will implement a dynamic proxy that will invoke the NullPropertyAssertion from the previous part of the assignment. The primary work will be in implementing an InvocationHandler implementation that will provide the implementation "advice" to the target object for selected methods. The advice will be a null check of specific properties of objects passed as parameters to the target object.

assignment3c homesales dynamicproxy
Figure 11. Dynamic Proxy Advice

The constructed Proxy will be used as a stepping stone to better understand the Aspect solution you will implement in the next section. The handler/proxy will not be used in the final solution. It will be instantiated and tested within the JUnit test using an injected HomesService and BuyersService from the Spring context.

No HomeDTO/BuyerDTO Compilation Dependency
There will be no mention of or direct dependency on Homes or Buyers in your solution. Everything will be accomplished using Java reflection.

6.2.3. Requirements

  1. implement the details for the NullValidatorHandler class that

    1. implements the java.lang.reflect.InvocationHandler interface

    2. has implementation properties

      1. nullPropertyAssertion — implements the check (from previous section)

      2. target object that it is the proxy for

      3. methodName it will operate against on the target object

      4. nullProperties — propertyNames it will test for null (used also in previous section)

      5. nonNullProperties — propertyNames it will test for non-null (used also in previous section)

    3. has a static builder method called newInstance that accepts the above properties and returns a java.lang.reflect.Proxy implementing all interfaces of the target

      In order for the tests to locate your factory method, it must have the following exact signature.

      //NullPropertyAssertion.class, Object.class, String.class, List.class, List.class
      public static <T> T newInstance(
              NullPropertyAssertion nullPropertyAssertion,
              T target,
              String methodName,
              List<String> nullProperties,
              List<String> nonNullProperties) {
      org.apache.commons.lang3.ClassUtils can be used to locate all interfaces of a class.
    4. implements the invoke() method of the InvocationHandler interface to validate the arguments passed to the method matching the methodName. Checks properties matching nullProperties and nonNullProperties.

      nullPropertyAssertion.assertConditions(arg, isNullProperties, true);
      nullPropertyAssertion.assertConditions(arg, nonNullProperties, false);
      1. if the arguments are found to be in error — let the nullPropertyAssertion throw its exception

      2. otherwise allow the call to continue to the target object/method with provided args

        Validator limits
        Each proxy instance will only validate parameters of the named method but all parameters to that method. There is no way to distinguish between param1.propertyName and param2.propertyName with our design and will not need to
  2. Use the provided JUnit tests (MyD2_DynamnicProxyNTest) verify the functionality of the requirements above once you activate.

6.2.4. Grading

Your solution will be evaluated on:

  1. create a JDK Dynamic Proxy to implement adhoc interface(s) to form a proxy at runtime for implementing advice

    1. whether you created a java.lang.reflect.InvocationHandler that would perform the validation on proxied targets

    2. whether you successfully created and returned a dynamic proxy from the newInstance factory method

    3. whether your returned proxy was able to successfully validate arguments passed to target

6.2.5. Additional Details

  1. The JUnit test case uses real HomeService and BuyerService @Autowired from the Spring context, but only instantiates the proxy as a POJO within the test case.

  2. The JUnit tests make calls to the HomeService and BuyerService, passing valid and invalid instances according to how your proxy was instantiated during the call to newInstance().

  3. The completed dynamic proxy will not be used beyond this section of the assignment. However, try to spot what the dynamic proxy has in common with the follow-on Aspect solution — since Spring interface Aspects are implemented using Dynamic Proxies.

6.3. Aspects

6.3.1. Purpose

In this portion of the assignment, you will demonstrate your knowledge of adding functionality to a point in code using an Aspect. You will:

  1. implement dynamically assigned behavior to methods using Spring Aspect-Oriented Programming (AOP) and AspectJ

  2. identify method join points to inject using pointcut expressions

  3. implement advice that executes before join points

  4. implement parameter injection into advice

6.3.2. Overview

In this portion of the assignment you will implement a ValidatorAspect that will "advise" service calls to provided secure Home/Buyer wrapper services.

The aspect will be part of your overall Spring context and will be able to change the behavior of the Homes and Buyers used within your runtime application and tests when activated. The aspect will specifically reject any object passed to these services that violate defined create/update constraints. The aspect will be defined to match the targeted methods and arguments but will have no compilation dependency that is specific to Homes or Buyers.

assignment3c homesales aspect
Figure 12. Aspect Advice
No HomeDTO/BuyerDTO Compilation Dependency
There will be no direct dependency on Homes or Buyers. Everything will be accomplished using AOP expressions and Java reflection constructs.

6.3.3. Requirements

  1. add AOP dependencies using the spring-boot-starter-aop module

  2. enable AspectJ auto proxy handling within your application

  3. create a ValidatorAspect component

    1. inject a NullPropertyAssertion bean and List<MethodConstraints> (populated from application-aop.yaml from the support module)

    2. make the class an Aspect

    3. make the overall component conditional on the aop profile

      This means the Home and Buyer services will act as delivered when the aop profile is not active and enforce the new constraints when the profile is active.
  4. define a "pointcut" that will target the calls to the SecureHomesWrapper and SecureBuyersWrapper services. This pointcut should both:

    1. define a match pattern for the "join point"

  5. define an "advice" method to execute before the "join point"

    1. uses the previously defined "pointcut" to identify its "join point"

    2. uses typed or dynamic advice parameters

      Using typed advice parameters will require a close relationship between advice and service methods. Using dynamic advice parameters (JoinPoint) will allow a single advice method be used for all service methods. The former is more appropriate for targeted behavior. The latter is more appropriate to general purpose behavior.
    3. invokes the NullPropertyAssertion bean with the parameters passed to the method and asks to validate for isNull and notNull.

      nullPropertyAssertion.assertConditions(arg, conditions.getIsNull(), true);
      nullPropertyAssertion.assertConditions(arg, conditions.getNotNull(), false);
  6. Use the provided JUnit test cases to verify completion of the above requirements

    1. the initial MyD3a1_NoAspectSvcNTest deactivates the aop profile to demonstrate baseline behavior we want to change/not-change

    2. the second MyD3a2_AspectSvcNTest activates the aop profile and asserts different results

    3. the third MyD3b_AspectWebNTest demonstrates that the aspect was added to the beans injected into the Homes and Buyers controllers.

      If AspectSvcNTest passes, and AspectWebNTest fails, check that you are advising the secure wrapper and not the base service.

6.3.4. Grading

Your solution will be evaluated on:

  1. implement dynamically assigned behavior to methods using Spring Aspect-Oriented Programming (AOP) and AspectJ

    1. whether you activated aspects in your solution

    2. whether you supplied an aspect as a component

  2. identify method join points to inject using pointcut expressions

    1. whether your aspect class contained a pointcut that correctly matched the target method join points

  3. implement advice that executes before join points

    1. whether your solution implements required validation before allowing target to execute

    2. whether your solution will allow the target to execute if validation passes

    3. whether your solution will prevent the target from executing and report the error if validation fails

  4. implement parameter injection into advice

    1. whether you have implemented typed or dynamic access to the arguments passed to the target method

6.3.5. Additional Details

  1. You are to base your AOP validation based on the data found within the injected List<MethodConstraints>. Each instance contains the name of the method and a list of property names that should be subject to isNull or notNull assertions.

    application-aop.yaml (in support)
    aop:
      validation:
        - methodName: createHome
          isNull: [id, username]
          notNull: [value, yearBuilt, bedRooms ]
        - methodName: updateHome
          isNull: [username]
    
        - methodName: createBuyer
          isNull: [id, username]
          notNull: [firstName, lastName, email]
        - methodName: updateBuyer
          isNull: [username]
  2. The JUnit test case uses real, secured HomesService and BuyerServices @Autowired from the Spring context augmented with aspects. Your aspect will be included when the aop profile is active.

  3. The JUnit tests will invoke the security service wrappers directly and through the controllers — calling with valid and invalid parameters according to the definition in the application-aop.yaml file.

    Note: The Home and Buyer services has some base validation built in and will not accept a non-null ID during the create calls. You cannot change that behavior and will not have to.

  4. Ungraded activity — create a breakpoint in the Advice and Homes/BuyersService(s) when executing the tests. Observe the call stack to see how you got to that point, where you are headed, and what else is in that call stack.