Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
79 changes: 7 additions & 72 deletions .github/workflows/workastra_platform.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ concurrency:

env:
REGISTRY: ghcr.io
JAVA_VERSION: 25.0.1
JAVA_DISTRIBUTION: graalvm
JAVA_VERSION: 26
JAVA_DISTRIBUTION: corretto

jobs:
qodana:
Expand All @@ -33,12 +33,8 @@ jobs:
- uses: JetBrains/qodana-action@v2026.1.0
with:
pr-mode: false
- if: always()
uses: github/codeql-action/upload-sarif@v4.35.4
with:
sarif_file: ${{ runner.temp }}/qodana/results/qodana.sarif.json

jvm-build:
verification:
name: "[${{ matrix.module }}] JVM build & test"
runs-on: ubuntu-24.04
permissions:
Expand All @@ -51,76 +47,15 @@ jobs:
steps:
- uses: actions/checkout@v6

- name: Set up GraalVM
- name: Set up JAVA
uses: actions/setup-java@v5
with:
java-version: ${{ env.JAVA_VERSION }}
distribution: ${{ env.JAVA_DISTRIBUTION }}
cache: gradle

- name: Test JVM
run: ./gradlew --no-daemon :${{ matrix.module }}:check

- name: Build JVM
run: ./gradlew --no-daemon :${{ matrix.module }}:build

native-test:
name: "[${{ matrix.module }}] Native tests"
runs-on: ubuntu-24.04
permissions:
contents: read
strategy:
fail-fast: false
matrix:
module: [console, iam, migration]
steps:
- uses: actions/checkout@v6

- name: Set up GraalVM
uses: actions/setup-java@v5
with:
java-version: ${{ env.JAVA_VERSION }}
distribution: ${{ env.JAVA_DISTRIBUTION }}
cache: gradle

- name: Run native tests
run: ./gradlew --no-daemon :${{ matrix.module }}:nativeTest

native-build:
name: "[${{ matrix.module }}] Native image build"
runs-on: ubuntu-24.04
permissions:
contents: read
packages: write
strategy:
fail-fast: false
matrix:
module: [console, iam, migration]
steps:
- uses: actions/checkout@v6

- name: Set up GraalVM
uses: actions/setup-java@v5
with:
java-version: ${{ env.JAVA_VERSION }}
distribution: ${{ env.JAVA_DISTRIBUTION }}
cache: gradle

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ github.repository }}/${{ matrix.module }}

- name: Build native image
uses: docker/build-push-action@v5
with:
context: .
file: ./${{ matrix.module }}/Dockerfile
push: false
provenance: false
sbom: false
cache-from: type=gha
cache-to: type=gha,mode=max
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
3 changes: 1 addition & 2 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ plugins {
checkstyle
id("org.springframework.boot") version "4.0.6" apply false
id("io.spring.dependency-management") version "1.1.7" apply false
id("org.graalvm.buildtools.native") version "0.11.4" apply false
id("com.diffplug.spotless") version "8.4.0"
id("net.ltgt.errorprone") version "5.1.0"
id("io.freefair.lombok") version "9.5.0"
Expand All @@ -29,7 +28,7 @@ subprojects {

java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(25))
languageVersion.set(JavaLanguageVersion.of(26))
}
}

Expand Down
1 change: 0 additions & 1 deletion console/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import org.springframework.boot.gradle.tasks.bundling.BootJar
plugins {
id("org.springframework.boot")
id("io.spring.dependency-management")
id("org.graalvm.buildtools.native")
}

dependencies {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.workastra.core.module.auditing;

import com.workastra.core.module.security.model.User;
import java.util.Optional;
import org.springframework.data.domain.AuditorAware;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;

@Component
public class AuditorAwareImpl implements AuditorAware<User> {

@Override
public Optional<User> getCurrentAuditor() {
return Optional.of(SecurityContextHolder.getContext())
.map(SecurityContext::getAuthentication)
.filter(Authentication::isAuthenticated)
.map(Authentication::getPrincipal)
.map(User.class::cast);
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@NullMarked
package com.workastra.core.infrastructure;
package com.workastra.core.module.auditing;

import org.jspecify.annotations.NullMarked;
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.workastra.core.infrastructure;
package com.workastra.core.module.lock;

import javax.sql.DataSource;
import org.springframework.boot.autoconfigure.AutoConfiguration;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@NullMarked
package com.workastra.iam.entity;
package com.workastra.core.module.lock;

import org.jspecify.annotations.NullMarked;
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.workastra.core.infrastructure;
package com.workastra.core.module.security.configuration;

import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
@NullMarked
package com.workastra.core.module.security.configuration;

import org.jspecify.annotations.NullMarked;
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.workastra.iam.entity;
package com.workastra.core.module.security.model;

import com.ibm.icu.text.PersonName;
import com.ibm.icu.text.PersonNameFormatter;
Expand All @@ -8,8 +8,11 @@
import com.ibm.icu.text.SimplePersonName;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.FetchType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import java.io.Serial;
import java.time.Instant;
Expand All @@ -23,9 +26,15 @@
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.hibernate.annotations.Generated;
import org.hibernate.annotations.UuidGenerator;
import org.hibernate.annotations.UuidGenerator.Style;
import org.hibernate.generator.EventType;
import org.jspecify.annotations.Nullable;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import org.springframework.security.core.CredentialsContainer;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
Expand All @@ -36,14 +45,16 @@
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@EntityListeners(AuditingEntityListener.class)
public class User implements UserDetails, CredentialsContainer {

@Serial
private static final long serialVersionUID = 1L;

@Id
@GeneratedValue
@Column
@UuidGenerator(style = Style.VERSION_7)
private UUID id;

@Column
Expand All @@ -60,10 +71,11 @@ public class User implements UserDetails, CredentialsContainer {
private @Nullable String middleName;

@Column
private String givenName;
private @Nullable String givenName;

@Column
private String gender;
@Builder.Default
private String gender = "other";

@Column
private String email;
Expand All @@ -75,42 +87,57 @@ public class User implements UserDetails, CredentialsContainer {
private Instant emailVerifiedAt;

@Column
private Locale locale;
@Builder.Default
private Locale locale = Locale.forLanguageTag("en-US");

@Getter(AccessLevel.NONE)
@Column
private boolean accountNonExpired;
@Builder.Default
private boolean accountNonExpired = true;

@Getter(AccessLevel.NONE)
@Column
private boolean accountNonLocked;
@Builder.Default
private boolean accountNonLocked = true;

@Getter(AccessLevel.NONE)
@Column
private boolean credentialsNonExpired;
@Builder.Default
private boolean credentialsNonExpired = true;

@Getter(AccessLevel.NONE)
@Column
private boolean enabled;
@Builder.Default
private boolean enabled = true;

@Column
private ZoneId timezoneId;
@Builder.Default
private ZoneId timezoneId = ZoneId.of("UTC");

@Column(insertable = false, updatable = false)
@Generated(event = { EventType.INSERT })
private Instant createdAt;

@Column(insertable = false, updatable = false)
@Generated(event = { EventType.INSERT })
private String createdBy;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "created_by", updatable = false)
@CreatedBy
private User createdBy;

@Column(insertable = false, updatable = false)
@Generated(event = { EventType.INSERT, EventType.UPDATE })
private Instant updatedAt;

@Column(insertable = false, updatable = false)
@Generated(event = { EventType.INSERT, EventType.UPDATE })
private String updatedBy;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "updated_by")
@LastModifiedBy
private User updatedBy;

@Column
private @Nullable Instant deletedAt;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "deleted_by")
private @Nullable User deletedBy;

@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Expand Down Expand Up @@ -153,9 +180,11 @@ public boolean isEnabled() {
}

public String getFullName() {
SimplePersonName.Builder personNameBuilder = SimplePersonName.builder()
.setLocale(this.locale)
.addField(PersonName.NameField.GIVEN, null, this.givenName);
if (this.familyName == null && this.middleName == null && this.givenName == null) {
return this.getUsername();
}

SimplePersonName.Builder personNameBuilder = SimplePersonName.builder().setLocale(this.locale);

if (this.familyName != null) {
personNameBuilder.addField(PersonName.NameField.SURNAME, null, this.familyName);
Expand All @@ -165,6 +194,10 @@ public String getFullName() {
personNameBuilder.addField(PersonName.NameField.GIVEN2, null, this.middleName);
}

if (this.givenName != null) {
personNameBuilder.addField(PersonName.NameField.GIVEN, null, this.givenName);
}

PersonNameFormatter formatter = PersonNameFormatter.builder()
.setLocale(this.locale)
.setLength(Length.LONG)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@NullMarked
package com.workastra.iam.repository;
package com.workastra.core.module.security.model;

import org.jspecify.annotations.NullMarked;
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.workastra.iam.repository;
package com.workastra.core.module.security.repository;

import com.workastra.iam.entity.User;
import com.workastra.core.module.security.model.User;
import java.util.UUID;
import org.jspecify.annotations.Nullable;
import org.springframework.data.repository.CrudRepository;
Expand All @@ -10,4 +10,7 @@
public interface UserRepository extends CrudRepository<User, UUID> {
@Nullable
User findByUsername(String username);

@Nullable
User findByUsernameAndDeletedAtIsNull(String username);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
@NullMarked
package com.workastra.core.module.security.repository;

import org.jspecify.annotations.NullMarked;
Loading
Loading