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

Subject identifier (sub) replaced email for user id (backend internal) #16

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
73 commits
Select commit Hold shift + click to select a range
0c48f2e
Subject identifier (sub) replaced email for user id (backend internal)
aiAdrian Jul 24, 2024
80a4031
test fixed
aiAdrian Jul 24, 2024
6b99021
test fixed
aiAdrian Jul 24, 2024
71a4dfe
Add test ProjectController pattern.
chenkins Jul 24, 2024
bbf19a8
review findings fixed
aiAdrian Jul 25, 2024
356861b
lint fix
aiAdrian Jul 25, 2024
c58eeb7
fix
aiAdrian Jul 25, 2024
811183e
fix
aiAdrian Jul 25, 2024
0b023fd
fix
aiAdrian Jul 25, 2024
f101db1
fix
aiAdrian Jul 25, 2024
3fba461
fix
aiAdrian Jul 25, 2024
4e26603
fix
aiAdrian Jul 25, 2024
3d3e0dc
fix
aiAdrian Jul 25, 2024
dfa5f74
fix
aiAdrian Jul 25, 2024
feca2f8
fix
aiAdrian Jul 25, 2024
b6effbe
fix
aiAdrian Jul 25, 2024
2adf522
fix
aiAdrian Jul 25, 2024
2e802b0
fix
aiAdrian Jul 25, 2024
d948e71
compilation fix
aiAdrian Jul 25, 2024
09748b8
Update src/main/java/ch/sbb/pfi/netzgrafikeditor/api/common/Authentic…
aiAdrian Jul 25, 2024
f62f684
compilation fix
aiAdrian Jul 25, 2024
199cfe8
code comment / doc added
aiAdrian Jul 25, 2024
d84078d
manualy merged
aiAdrian Jul 25, 2024
be656e5
pattern and test
aiAdrian Jul 25, 2024
e416595
pattern and test
aiAdrian Jul 25, 2024
1c893ca
error fixed - [email protected] => has to fail
aiAdrian Jul 25, 2024
aa558d5
Merge branch '15-feature-request-shareing-through-email-identifier-in…
aiAdrian Jul 25, 2024
9388643
Merge pull request #17 from chenkins/15-feature-request-shareing-thro…
aiAdrian Jul 25, 2024
f373bd1
Update src/main/java/ch/sbb/pfi/netzgrafikeditor/api/project/ProjectC…
aiAdrian Jul 25, 2024
205ff65
Update ProjectControllerTest.java
aiAdrian Jul 25, 2024
7519b3e
test backwards compatiblity
aiAdrian Jul 29, 2024
1455684
test backwards compatiblity
aiAdrian Jul 29, 2024
bac7080
condition and( ... or )
aiAdrian Jul 29, 2024
02e970d
code formating issue fixed?
aiAdrian Jul 29, 2024
7be4280
code formating issue fixed?
aiAdrian Jul 29, 2024
f95a7e8
auth fix
aiAdrian Jul 29, 2024
53d45d2
check auth
aiAdrian Jul 29, 2024
f22eb55
test union left join
aiAdrian Jul 29, 2024
678b2ed
undo
aiAdrian Jul 29, 2024
0264ec6
backward comptability
aiAdrian Jul 29, 2024
46dadd0
backward comptability - formatting issue
aiAdrian Jul 29, 2024
25125a5
backward comptability - formatting issue
aiAdrian Jul 29, 2024
48f70fc
lint issue , formatting fix
aiAdrian Jul 29, 2024
f67d245
lint issue , formatting fix
aiAdrian Jul 29, 2024
174f1a0
lint issue , formatting fix
aiAdrian Jul 29, 2024
3bd58d3
lint issue , formatting fix
aiAdrian Jul 29, 2024
5528d06
lint issue , formatting fix
aiAdrian Jul 29, 2024
98c6675
typo fixed
aiAdrian Jul 29, 2024
a25adea
typo fixed
aiAdrian Jul 29, 2024
faf41ef
typo fixed
aiAdrian Jul 29, 2024
88643c3
typo fixed
aiAdrian Jul 29, 2024
02866cc
typo fixed
aiAdrian Jul 29, 2024
f745d77
typo fixed
aiAdrian Jul 29, 2024
3771983
typo fixed
aiAdrian Jul 29, 2024
db125a3
reg fix
aiAdrian Jul 29, 2024
09b2381
test
aiAdrian Jul 29, 2024
dc01cd6
test
aiAdrian Jul 29, 2024
24bce4d
alle user id has to be lower case (emails)
aiAdrian Jul 31, 2024
0a43987
include issue fixed : ListIterator
aiAdrian Jul 31, 2024
13c75e7
include issue fixed : ListIterator
aiAdrian Jul 31, 2024
7f4feb3
include issue fixed : ListIterator
aiAdrian Jul 31, 2024
b2d16be
enfoce to toLowerCase instead of upper case
aiAdrian Jul 31, 2024
b3d55ec
enfoce to toLowerCase instead of upper case
aiAdrian Jul 31, 2024
0bf5928
Merge pull request #18 from SchweizerischeBundesbahnen/15-check-backw…
aiAdrian Jul 31, 2024
bcc8280
check if test fails
aiAdrian Jul 31, 2024
dc015b2
check if test fails
aiAdrian Jul 31, 2024
723a6f7
this change doesn't affect the tests
aiAdrian Jul 31, 2024
43fad5b
this change doesn't affect the tests
aiAdrian Jul 31, 2024
8e617e3
this change doesn't affect the tests
aiAdrian Jul 31, 2024
cc85b5a
this change doesn't affect the tests
aiAdrian Jul 31, 2024
194d0f6
this change doesn't affect the tests
aiAdrian Jul 31, 2024
5c275f2
code formatted
aiAdrian Jul 31, 2024
1f08cef
New access checks (auth) is used to get getAuthorizationInfo -> when …
Jul 31, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import lombok.Value;
import lombok.val;

import org.jooq.Condition;
import org.jooq.DSLContext;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.GrantedAuthority;
Expand All @@ -32,20 +33,36 @@
@Service
@RequiredArgsConstructor
public class AuthenticationService {
private static final String USER_ID_CLAIM = "sub";
private static final String EMAIL_CLAIM = "preferred_username";

// See: https://openid.net/specs/openid-connect-core-1_0.html#Claims
private static final String USER_ID_FROM_EMAIL_CLAIM = "email";
private static final String Subject_Identifier_CLAIM = "sub";
private static final String PREFERRED_USERNAME_CLAIM = "preferred_username";

private final DSLContext context;

public String getCurrentUserEmail() {
return this.tryGetClaim(EMAIL_CLAIM)
.orElseThrow(() -> new BadCredentialsException("E-Mail missing in token"));
return this.tryGetClaim(PREFERRED_USERNAME_CLAIM)
.orElseThrow(() -> new BadCredentialsException("E-Mail missing in token"))
.toLowerCase();
}

public UserId getCurrentUserIdFromEmail() {
val idString =
this.tryGetClaim(USER_ID_FROM_EMAIL_CLAIM)
.orElseThrow(() -> new BadCredentialsException("User ID missing in token"))
.toLowerCase();

return UserId.of(idString);
}

public UserId getCurrentUserId() {
public UserId getCurrentSubjectId() {
val idString =
this.tryGetClaim(USER_ID_CLAIM)
.orElseThrow(() -> new BadCredentialsException("User ID missing in token"));
this.tryGetClaim(Subject_Identifier_CLAIM)
.orElseThrow(
() ->
new BadCredentialsException(
"Sub identifier missing in token"));

return UserId.of(idString);
}
Expand All @@ -66,14 +83,26 @@ private Optional<String> tryGetClaim(String claim) {
}

public AuthorizationInfo getAuthorizationInfo(ProjectId projectId) throws NotFoundException {
Condition whereCondition =
this.isAdmin()
? PROJECTS.ID.eq(projectId.getValue())
: PROJECTS.ID
.eq(projectId.getValue())
.and(
PROJECTS_USERS
.USER_ID
.eq(this.getCurrentUserIdFromEmail().getValue())
.or(
PROJECTS_USERS.USER_ID.eq(
this.getCurrentSubjectId()
.getValue())));
return this.context
.select(PROJECTS.IS_ARCHIVED, PROJECTS_USERS.IS_EDITOR)
.from(PROJECTS)
.leftJoin(PROJECTS_USERS)
.on(
PROJECTS_USERS.PROJECT_ID.eq(PROJECTS.ID),
PROJECTS_USERS.USER_ID.eq(this.getCurrentUserId().getValue()))
.where(PROJECTS.ID.eq(projectId.getValue()))
.on(PROJECTS_USERS.PROJECT_ID.eq(PROJECTS.ID))
.where(whereCondition)
.limit(1)
.fetchOptional()
.map(
record -> {
Expand All @@ -86,16 +115,28 @@ record -> {
}

public AuthorizationInfo getAuthorizationInfo(VariantId variantId) throws NotFoundException {
Condition whereCondition =
this.isAdmin()
? VARIANTS.ID.eq(variantId.getValue())
: VARIANTS.ID
.eq(variantId.getValue())
.and(
PROJECTS_USERS
.USER_ID
.eq(this.getCurrentUserIdFromEmail().getValue())
.or(
PROJECTS_USERS.USER_ID.eq(
this.getCurrentSubjectId()
.getValue())));
return this.context
.select(PROJECTS.IS_ARCHIVED, VARIANTS.IS_ARCHIVED, PROJECTS_USERS.IS_EDITOR)
.from(PROJECTS)
.join(VARIANTS)
.on(VARIANTS.PROJECT_ID.eq(PROJECTS.ID))
.leftJoin(PROJECTS_USERS)
.on(
PROJECTS_USERS.PROJECT_ID.eq(PROJECTS.ID),
PROJECTS_USERS.USER_ID.eq(this.getCurrentUserId().getValue()))
.where(VARIANTS.ID.eq(variantId.getValue()))
.on(PROJECTS_USERS.PROJECT_ID.eq(PROJECTS.ID))
.where(whereCondition)
.limit(1)
.fetchOptional()
.map(
record -> {
Expand All @@ -112,6 +153,19 @@ record -> {
}

public AuthorizationInfo getAuthorizationInfo(VersionId versionId) throws NotFoundException {
Condition whereCondition =
this.isAdmin()
? VERSIONS.ID.eq(versionId.getValue())
: VERSIONS.ID
.eq(versionId.getValue())
.and(
PROJECTS_USERS
.USER_ID
.eq(this.getCurrentUserIdFromEmail().getValue())
.or(
PROJECTS_USERS.USER_ID.eq(
this.getCurrentSubjectId()
.getValue())));
return this.context
.select(PROJECTS.IS_ARCHIVED, VARIANTS.IS_ARCHIVED, PROJECTS_USERS.IS_EDITOR)
.from(PROJECTS)
Expand All @@ -120,10 +174,9 @@ public AuthorizationInfo getAuthorizationInfo(VersionId versionId) throws NotFou
.join(VERSIONS)
.on(VERSIONS.VARIANT_ID.eq(VARIANTS.ID))
.leftJoin(PROJECTS_USERS)
.on(
PROJECTS_USERS.PROJECT_ID.eq(PROJECTS.ID),
PROJECTS_USERS.USER_ID.eq(this.getCurrentUserId().getValue()))
.where(VERSIONS.ID.eq(versionId.getValue()))
.on(PROJECTS_USERS.PROJECT_ID.eq(PROJECTS.ID))
.where(whereCondition)
.limit(1)
.fetchOptional()
.map(
record -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;

import java.util.Collection;
import java.util.ListIterator;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

Expand All @@ -35,11 +36,15 @@
public class ProjectController {
private final ProjectService projectService;

private static Pattern USER_ID_PATTERN = Pattern.compile("^(u|ue|e)\\d+$");
// email adress validator: regex to match emails using the expression
protected static Pattern USER_ID_AS_EMAIL_PATTERN =
Pattern.compile(
"^([a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*)|(u|ue|e)\\d+$");

@PostMapping("/v1/projects")
public ResponseEntity<Long> createProject(@RequestBody ProjectCreateUpdateDto projectDto)
throws ValidationErrorException {
this.convertAllUsersToLowerCase(projectDto);
this.assertValidUserIds(projectDto);

val id = this.projectService.create(projectDto);
Expand Down Expand Up @@ -76,6 +81,7 @@ public ResponseEntity<Void> deleteProject(@PathVariable ProjectId projectId)
public ResponseEntity<Void> updateProject(
@PathVariable ProjectId id, @Valid @RequestBody ProjectCreateUpdateDto projectDto)
throws NotFoundException, ValidationErrorException, ForbiddenOperationException {
this.convertAllUsersToLowerCase(projectDto);
this.assertValidUserIds(projectDto);
this.projectService.update(id, projectDto);
return ResponseEntity.noContent().build();
Expand All @@ -87,10 +93,21 @@ private void assertValidUserIds(ProjectCreateUpdateDto projectDto)
this.assertValidUserIds(projectDto.getWriteUsers());
}

private void convertAllUsersToLowerCase(ProjectCreateUpdateDto projectDto) {
ListIterator<String> iteratorWriteUsers = projectDto.getWriteUsers().listIterator();
while (iteratorWriteUsers.hasNext()) {
iteratorWriteUsers.set(iteratorWriteUsers.next().toLowerCase());
}
ListIterator<String> iteratorReadUsers = projectDto.getReadUsers().listIterator();
while (iteratorReadUsers.hasNext()) {
iteratorReadUsers.set(iteratorReadUsers.next().toLowerCase());
}
}

private void assertValidUserIds(Collection<String> userIds) throws ValidationErrorException {
val invalidUserIds =
userIds.stream()
.filter(id -> !USER_ID_PATTERN.matcher(id).matches())
.filter(id -> !USER_ID_AS_EMAIL_PATTERN.matcher(id).matches())
.collect(Collectors.toList());

if (!invalidUserIds.isEmpty()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,15 +59,16 @@ val record =
.setDescription(project.getDescription())
.setIsArchived(false)
.setCreatedAt(nowProvider.now())
.setCreatedBy(this.authenticationService.getCurrentUserId().getValue());
.setCreatedBy(
this.authenticationService.getCurrentUserIdFromEmail().getValue());

record.store();

val projectId = ProjectId.of(record.getId());

var writeUsersIncludingCurrentUser = new ArrayList<>(project.getWriteUsers());
writeUsersIncludingCurrentUser.add(
this.authenticationService.getCurrentUserId().getValue());
this.authenticationService.getCurrentUserIdFromEmail().getValue());

this.updateProjectUsers(projectId, writeUsersIncludingCurrentUser, project.getReadUsers());

Expand Down Expand Up @@ -184,11 +185,22 @@ public Collection<ProjectSummaryDto> getAll() {
: selectOne()
.from(PROJECTS_USERS)
.where(
PROJECTS_USERS.PROJECT_ID.eq(PROJECTS.ID),
PROJECTS_USERS.USER_ID.eq(
this.authenticationService
.getCurrentUserId()
.getValue()));
PROJECTS_USERS
.PROJECT_ID
.eq(PROJECTS.ID)
.and(
PROJECTS_USERS
.USER_ID
.eq(
this.authenticationService
.getCurrentUserIdFromEmail()
.getValue())
.or(
PROJECTS_USERS.USER_ID.eq(
this
.authenticationService
.getCurrentSubjectId()
.getValue()))));

return this.context
.selectFrom(PROJECTS)
Expand Down Expand Up @@ -227,7 +239,9 @@ public ProjectDto getById(ProjectId projectId)

for (val snapshotVersion :
this.getLatestSnapshotVersions(
projectId, authenticationService.getCurrentUserId())) {
projectId,
authenticationService.getCurrentUserIdFromEmail(),
authenticationService.getCurrentSubjectId())) {
variantsMap
.get(snapshotVersion.getVariantId())
.latestSnapshotVersion(Optional.of(snapshotVersion));
Expand Down Expand Up @@ -306,7 +320,8 @@ private List<VersionDto> getLatestReleaseVersions(ProjectId projectId) {
.collect(Collectors.toList());
}

private List<VersionDto> getLatestSnapshotVersions(ProjectId projectId, UserId userId) {
private List<VersionDto> getLatestSnapshotVersions(
ProjectId projectId, UserId userId, UserId subId) {
return this.context
.select(VERSIONS.asterisk())
.distinctOn(VERSIONS.VARIANT_ID)
Expand All @@ -316,7 +331,9 @@ private List<VersionDto> getLatestSnapshotVersions(ProjectId projectId, UserId u
.where(
VARIANTS.PROJECT_ID.eq(projectId.getValue()),
VERSIONS.SNAPSHOT_VERSION.isNotNull(),
VERSIONS.CREATED_BY.eq(userId.getValue()))
VERSIONS.CREATED_BY
.eq(userId.getValue())
.or(VERSIONS.CREATED_BY.eq(subId.getValue())))
.orderBy(
VERSIONS.VARIANT_ID,
VERSIONS.RELEASE_VERSION.desc(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public VariantId create(ProjectId projectId, VariantCreateDto createDto)
this.authenticationService.getAuthorizationInfo(projectId).assertWritable();

val now = this.nowProvider.now();
val userId = this.authenticationService.getCurrentUserId();
val userId = this.authenticationService.getCurrentUserIdFromEmail();

val variantRecord =
this.context
Expand Down Expand Up @@ -131,7 +131,7 @@ val record =
this.authenticationService.getAuthorizationInfo(projectId).assertWritable();

val now = this.nowProvider.now();
val userId = this.authenticationService.getCurrentUserId();
val userId = this.authenticationService.getCurrentUserIdFromEmail();

val variantRecord =
this.context
Expand Down Expand Up @@ -206,7 +206,7 @@ private VariantDto map(
public void dropSnapshots(VariantId variantId) throws NotFoundException {
assertVariantExists(variantId);

val userId = this.authenticationService.getCurrentUserId();
val userId = this.authenticationService.getCurrentUserIdFromEmail();

if (this.hasReleases(variantId)) {
this.deleteAllSnapshots(variantId, userId);
Expand All @@ -217,7 +217,14 @@ public void dropSnapshots(VariantId variantId) throws NotFoundException {
.where(
VERSIONS.VARIANT_ID
.eq(variantId.getValue())
.and(VERSIONS.CREATED_BY.eq(userId.getValue()))
.and(
VERSIONS.CREATED_BY
.eq(userId.getValue())
.or(
VERSIONS.CREATED_BY.eq(
this.authenticationService
.getCurrentSubjectId()
.getValue())))
.and(VERSIONS.SNAPSHOT_VERSION.isNotNull())
.and(VERSIONS.SNAPSHOT_VERSION.notEqual(1))
.and(VERSIONS.RELEASE_VERSION.eq(1)))
Expand All @@ -236,7 +243,7 @@ public void raiseSnapshotsToNewestReleaseVersion(VariantId variantId)
assertVariantExists(variantId);
assertHasReleases(variantId);

val userId = this.authenticationService.getCurrentUserId();
val userId = this.authenticationService.getCurrentUserIdFromEmail();

val highestReleasedVersion =
this.tryFetchHighestReleaseVersion(variantId)
Expand Down
Loading