Skip to content
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

feat: implement authentication layer #18428

Merged
merged 24 commits into from
May 23, 2024
Merged

Conversation

Ben-Sheppard
Copy link
Contributor

@Ben-Sheppard Ben-Sheppard commented May 10, 2024

Closes https://github.com/camunda-cloud/identity/issues/2804

Description

This PR achieves a couple of tasks out of necessity so its a little larger than I'd often like, still, here goes:

  1. Implements a new auth-starter module under Identity that provides a centralised configuration for auth related classes
  2. Harmonizes some existing backend code with the new auth layer by way of a identity-common module
  3. Introduces configuration support for postgres and H2 by way of a profile postgres, h2
  4. Introduces the schema initializaiton for the user tables

Things that I want to highlight:

  1. We need to look for database schema migration support, the changes in this PR are fine for now to get foundations in but longer term this will cause issues
  2. I have tried to use a naming pattern of Camunda where there are classes that we extend from Spring so that its obvious its our code
  3. The error handler is taken from the existing /dist approach and I've also tried to align the configurations
  4. I have tested the code in this PR in two contexts, Identity + H2 and Identity + Standalone Broker + Postgres, my local tests have been successful for basic auth.

@github-actions github-actions bot added component/zeebe Related to the Zeebe component/team component/operate Related to the Operate component/team component/tasklist Related to the Tasklist component/team component/identity Related to the Identity component/team labels May 10, 2024
@Ben-Sheppard Ben-Sheppard force-pushed the implement-authentication-layer branch from 10c26b6 to bbe8387 Compare May 10, 2024 11:15
Copy link
Contributor

github-actions bot commented May 10, 2024

Tasklist Test Results

549 tests   544 ✅  1h 31m 52s ⏱️
143 suites    5 💤
143 files      0 ❌

Results for commit bf50761.

♻️ This comment has been updated with latest results.

Copy link
Contributor

github-actions bot commented May 10, 2024

Operate Test Results

265 tests   264 ✅  34s ⏱️
 42 suites    1 💤
 42 files      0 ❌

Results for commit 3fa08d5.

♻️ This comment has been updated with latest results.

Copy link
Contributor

@maryarm maryarm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The structure looks good to me, I have a few suggestions that you can find in following comments.

@Profile("identity-oidc-auth")
public class OidcCurrentUserService implements CurrentUserService {
@Override
public CamundaUser getCurrentUser() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm thinking if we need a separate implementation for oidc, as the authorities can be retrieved from authentication object (it would be an instance of OAuth2AuthenticationToken). The only difference is using authentication.name which extracted from claims["name"] instead of principal.getPreferredUsername which filled from claims["preferred_username"].

final OidcUser oidcUser = super.loadUser(userRequest);
final UserDetails details = userDetailsManager.loadUserByUsername(oidcUser.getName());
if (details != null) {
return new CamundaOidcUser(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Intellij suggests to use map(GrantedAuthority.class::cast)

}

@Override
protected List<GrantedAuthority> loadUserAuthorities(final String username) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this override for the current issue?
if it's required I suggest to give a try to override JdbcDaoImpl#addCustomAuthorities.
but if not, it's better to remove it for now and make it working later with the tasks related to add role/permission to identity.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need it (I just checked) so I'm removing it :)

Copy link
Contributor

github-actions bot commented May 20, 2024

Operate Opensearch ITs Results

182 tests   182 ✅  54s ⏱️
 19 suites    0 💤
 19 files      0 ❌

Results for commit bf50761.

♻️ This comment has been updated with latest results.

Copy link
Contributor

github-actions bot commented May 20, 2024

Operate Integration Tests Results

523 tests   521 ✅  8m 20s ⏱️
 88 suites    2 💤
 88 files      0 ❌

Results for commit bf50761.

♻️ This comment has been updated with latest results.

Copy link
Contributor

github-actions bot commented May 20, 2024

Operate Unit Tests Results

275 tests   274 ✅  6m 21s ⏱️
 44 suites    1 💤
 44 files      0 ❌

Results for commit bf50761.

♻️ This comment has been updated with latest results.

@Ben-Sheppard Ben-Sheppard force-pushed the implement-authentication-layer branch from 4d4d042 to 2219be1 Compare May 20, 2024 08:17
@Ben-Sheppard Ben-Sheppard requested a review from maryarm May 20, 2024 10:40
Copy link
Contributor

@dlavrenuek dlavrenuek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the code. The current combination of basic auth (local user management) and OIDC is confusing since it is not in the scope of the issue, so I recommend removing OIDC here.

@@ -5,11 +5,10 @@
* Licensed under the Camunda License 1.0. You may not use this file
* except in compliance with the Camunda License 1.0.
*/
package io.camunda.identity.usermanagement;
package io.camunda.identity.authentication;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need identity in the folder structure / class names? This way we have identity and authentication as (sub)domains. If we consider identity as the top level domain here, then it would make sense to split the internals in authentication and authorization. Otherwise we could just have a top level domain authentication and omit identity. What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Initially had included identity because its our realm and Identity is still a component in the stack in the same way we have io.camunda.operate... etc so I'd prefer to keep it, but I can see how we could remove this and have a flat level of io.camunda.authentication. Ultimately I don't feel strongly either way so I will change it if you have stronger feelings than me :D

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and Identity is still a component

That is also a reason to not use identity here, so it is not confused with the application/component in the end. On a scale of 1-10 I am at about 6.5 that we should not use identity here, but just keep it simple as io.camunda.authentication

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I've changed it to io.camunda.authentication

* Licensed under the Camunda License 1.0. You may not use this file
* except in compliance with the Camunda License 1.0.
*/
package io.camunda.identity.record;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest structuring the code by the domain instead of class type, so for example having a users directory with all user related records and services instead of service and records directories. This structure should be more robust as the code grows. In Identity it became challenging to name classes in a single directory - especially records as these could include entities but also request objects and DTOs. What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good, I've updated this structure now

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea of naming the packages with subdomain sounds good but I have some concerns here regarding how to find a subdomain that contains all relevant stuffs. For example when we named this package as user, then when we want to add group dtos we need to add a new package with one class only that's not looks nice, also for user roles and group roles and group users how we can proceed with this style?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An example structure for roles: everything related to roles would live in a roles directory. This could be a subdirectory of users or on the same level and would contain all DTOs and services to retrieve/manage roles. Working with subdirectories can give more granularity and a structure similar to api endpoints while having these on the same level should be easier for reusable parts like.
An example why I think that structuring by domain is a good idea is multitenancy. We would have a directory tenants containing all controllers, services, records, DTOs etc... and all of these can be annotated to only be included if multitenancy is activated. Otherwise the multitenancy annotation would be present on different classes in different directories and would be harder to follow

@@ -0,0 +1,10 @@
services:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like this file is only for testing, but otherwise I can't see any instructions on how to use it. Should we remove it here and rather add it as part of automatic test coverage?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file should be removed, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ugh yea, sorry I removed it and then added it back in for local testing 🤦🏻


@Bean
@Primary
@Profile("auth-basic")
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to have this here as well as on the class level above? 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good spot, that was left over from when we had multiple applicable beans in here, I'll remove

Copy link
Contributor

@maryarm maryarm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I only have one concern regarding the package names but it's not necessary to be addressed in this PR. Otherwise it looks good to me.

Comment on lines +57 to +75
return httpSecurity
.authorizeHttpRequests(
(authorizeHttpRequests) ->
authorizeHttpRequests
.requestMatchers(UNAUTHENTICATED_PATHS)
.permitAll()
.anyRequest()
.authenticated())
.headers(
(headers) ->
headers.httpStrictTransportSecurity(
(httpStrictTransportSecurity) ->
httpStrictTransportSecurity
.includeSubDomains(true)
.maxAgeInSeconds(63072000)
.preload(true)))
.exceptionHandling(
(exceptionHandling) -> exceptionHandling.accessDeniedHandler(authFailureHandler))
.csrf(AbstractHttpConfigurer::disable)

Check failure

Code scanning / CodeQL

Disabled Spring CSRF protection High

CSRF vulnerability due to protection being disabled.
Copy link
Contributor

@dlavrenuek dlavrenuek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 🚀

@Ben-Sheppard Ben-Sheppard added this pull request to the merge queue May 23, 2024
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks May 23, 2024
@Ben-Sheppard Ben-Sheppard added this pull request to the merge queue May 23, 2024
Merged via the queue into main with commit d262c29 May 23, 2024
62 of 64 checks passed
@Ben-Sheppard Ben-Sheppard deleted the implement-authentication-layer branch May 23, 2024 19:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
component/identity Related to the Identity component/team component/operate Related to the Operate component/team component/tasklist Related to the Tasklist component/team component/zeebe Related to the Zeebe component/team
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants