⋮IWConnect Java Team’s Open Source Contribution and Journey: Skill Enhancement and Global Impact

22 Sep, 2023 | 5 minutes read

Making your first open source contribution can be an intimidating but rewarding experience. That is why the IWConnect Java Team took the plunge and contributed code to the popular Spring framework, sharpening our skills and leaving a global impact.

Inspired by a conference session on open source, the team, and myself reviewed Spring issues on GitHub, found some documentation needing modernization, and submitted pull requests to update the code samples from XML to Java. Our contributions were merged, improving Spring for developers worldwide. This post shares our team’s journey into open source and how you can get started contributing to projects like Spring too.

Motivation & Inspiration to Contribute to Open Source

⋮IWConnect’s Java Team has implemented numerous features and resolved countless business problems using open-source frameworks, specifically Spring. The primary purpose of those frameworks is, and will continue to be, assisting developers in solving their problems more efficiently. They offer pre-built functionalities that developers can leverage to address their challenges.

The Spring ecosystem is a family of frameworks that provide solutions for nearly every problem. That is why, after many years, it remains one of the most widely used frameworks in the Java community. At ⋮IWConnect, we recognized that the Spring IO Conference in Barcelona would present a fantastic opportunity for us to acquire new knowledge. Driven by this initiative, we attended the conference.

Among the many excellent sessions, one, in particular, left a lasting impression on us – the Open-source Panel Discussion. In this session, a group of enthusiasts and members of the Spring team discussed open source. That inspired me to make a commitment that when I returned home and had some free time, I’d start an initiative to contribute to the Spring ecosystem, along with my teammates.

⋮IWConnect’s Java Team Contribution

Inspired by the conference, we began exploring how we could contribute to Spring projects. All Spring projects are hosted on GitHub, and each project has open issues available for contributions.

The first step the team took was to open these projects and review the reported issues. We identified two issues in the Spring Security project that I believed we could contribute to. After assessing the required work, the team started working on these issues.

The initial step for contributing to these projects is to sign a contributor license agreement. Once that’s done, you can fork the repository, create a branch, address the issue, and submit a pull request. That’s all it takes to get started.

Issue #1

The RememberMe documentation lacks comprehensive configuration samples, currently only including XML configuration fragments. It should be enhanced to provide a more modern approach to configuration, utilizing Java Configuration and defining the beans from a Java class.

We all know that XML is outdated, and Spring transitioned from XML configurations to Java bean configurations a long time ago. However, the documentation has not been updated accordingly.

Our team opened the documentation, examined the outdated XML provided, and translated it into Java code.

Here is the XML that needed updating:

<bean id="rememberMeFilter" class=
"org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter">
<property name="rememberMeServices" ref="rememberMeServices"/>
<property name="authenticationManager" ref="theAuthenticationManager" />
</bean>

<bean id="rememberMeServices" class=
"org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices">
<property name="userDetailsService" ref="myUserDetailsService"/>
<property name="key" value="springRocks"/>
</bean>

<bean id="rememberMeAuthenticationProvider" class=
"org.springframework.security.authentication.RememberMeAuthenticationProvider">
<property name="key" value="springRocks"/>

They translated this into Java-style configuration, resulting in:

@Bean
RememberMeAuthenticationFilter rememberMeFilter() {
    RememberMeAuthenticationFilter rememberMeFilter = new RememberMeAuthenticationFilter();
    rememberMeFilter.setRememberMeServices(rememberMeServices());
    rememberMeFilter.setAuthenticationManager(theAuthenticationManager);
    return rememberMeFilter;
}

@Bean
TokenBasedRememberMeServices rememberMeServices() {
    TokenBasedRememberMeServices rememberMeServices = new TokenBasedRememberMeServices();
    rememberMeServices.setUserDetailsService(myUserDetailsService);
    rememberMeServices.setKey("springRocks");
    return rememberMeServices;
}

@Bean
RememberMeAuthenticationProvider rememberMeAuthenticationProvider() {
    RememberMeAuthenticationProvider rememberMeAuthenticationProvider = new RememberMeAuthenticationProvider();
    rememberMeAuthenticationProvider.setKey("springRocks");
    return rememberMeAuthenticationProvider;
}

They raised the pull request, and it was successfully merged. Now if you visit this section in the official documentation you will find the code ?.

Issue Number 2

Issue #2 that the team tackled is as follows: 

Add hasScope for authorization rules

AuthorizationManager makes the following possible in the DSL:

http
    .authorizeHttpRequests((authorize) -> authorize
        .anyRequest().access(hasRole("USER"))
    )

It would be nice to have the same capability with scopes, like this:

http
    .authorizeHttpRequests((authorize) -> authorize
        .anyRequest().access(hasScope("resource:read"))
    )

This could be done in a static factory class, like OAuth2AuthorizationManagers in oauth2-core,which could have hasScope and hasAnyScope methods.

The pull request is still open, but hopefully, it will be merged soon.

Here is the implementation:

public final class OAuth2AuthorizationManagers {

    private OAuth2AuthorizationManagers() {
    }

    public static <T> AuthorityAuthorizationManager<T> hasScope(String scope) {
       verifyScope(scope);
       return AuthorityAuthorizationManager.hasAuthority("SCOPE_" + scope);
    }

    public static <T> AuthorityAuthorizationManager<T> hasAnyScope(String... scopes) {
       verifyScopes(scopes);
       String[] mappedScopes = new String[scopes.length];
       for (int i = 0; i < scopes.length; i++) {
          mappedScopes[i] = "SCOPE_" + scopes[i];
       }
       return AuthorityAuthorizationManager.hasAnyAuthority(mappedScopes);
    }

    private static void verifyScopes(String... scopes) throws IllegalArgumentException {
       for (String scope : scopes) {
          verifyScope(scope);
       }
    }

    private static void verifyScope(String scope) {
       if (scope.startsWith("SCOPE_")) {
          throw new IllegalArgumentException("Scope '" + scope + "' start with 'SCOPE_' prefix.");
       }
    }

}

The team received feedback from the reviewer to add unit tests, and so they did.

Here are the unit tests:

public class OAuth2AuthorizationManagersTests {

    @Test
    void hasScope_withInvalidScope_shouldThrowIllegalArgumentException() {
       String scope = "SCOPE_invalid";
       assertThatExceptionOfType(IllegalArgumentException.class)
             .isThrownBy(() -> OAuth2AuthorizationManagers.hasScope(scope))
             .withMessage("Scope 'SCOPE_invalid' start with 'SCOPE_' prefix.");
    }

    @Test
    void hasScopes_withInvalidScope_shouldThrowIllegalArgumentException() {
       String[] scopes = { "read", "write", "SCOPE_invalid" };
       assertThatExceptionOfType(IllegalArgumentException.class)
             .isThrownBy(() -> OAuth2AuthorizationManagers.hasAnyScope(scopes))
             .withMessage("Scope 'SCOPE_invalid' start with 'SCOPE_' prefix.");
    }

    @Test
    void hasScope_withValidScope_shouldPass() {
       String scope = "read";
       AuthorityAuthorizationManager<Object> authorizationManager = OAuth2AuthorizationManagers.hasScope(scope);
       assertThat(authorizationManager).isNotNull();
    }

    @Test
    void hasScope_withValidScopes_shouldPass() {
       String[] scopes = { "read", "write" };
       AuthorityAuthorizationManager<Object> authorizationManager = OAuth2AuthorizationManagers.hasAnyScope(scopes);
       assertThat(authorizationManager).isNotNull();
    }

}

Conclusion

Contributing to an open-source project is an excellent way to sharpen your development skills and engage in discussions, meet, and collaborate with the active project team. The feeling is also amazing, knowing that you’ve contributed to a project used worldwide and have made life easier for fellow developers. This was the primary reason why my team and I took the lead on this amazing initiative and made a significant contribution to the Spring Security project.

That is all for now.

We hope you will be inspired by our initiative, and that you will begin to contribute to your favorite open-source project.

Happy coding!