Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
a3357d9
fix(OFJAVA-033): 41 review findings across 30 files
flamingo[bot] Sep 14, 2026
0736d42
fix(OFJAVA-033): 41 review findings across 30 files
flamingo[bot] Sep 14, 2026
bac485b
fix(OFJAVA-033): 41 review findings across 30 files
flamingo[bot] Sep 14, 2026
9640769
fix(OFJAVA-033): 41 review findings across 30 files
flamingo[bot] Sep 14, 2026
025890d
fix(OFJAVA-033): 41 review findings across 30 files
flamingo[bot] Sep 14, 2026
c79adbb
fix(OFJAVA-033): 41 review findings across 30 files
flamingo[bot] Sep 14, 2026
1f125e5
fix(OFJAVA-033): 41 review findings across 30 files
flamingo[bot] Sep 14, 2026
a34b10d
fix(OFJAVA-033): 41 review findings across 30 files
flamingo[bot] Sep 14, 2026
cc718e6
fix(OFJAVA-033): 41 review findings across 30 files
flamingo[bot] Sep 14, 2026
e891805
fix(OFJAVA-033): 41 review findings across 30 files
flamingo[bot] Sep 14, 2026
79fb478
fix(OFJAVA-033): 41 review findings across 30 files
flamingo[bot] Sep 14, 2026
6e392f1
fix(OFJAVA-033): 41 review findings across 30 files
flamingo[bot] Sep 14, 2026
726ab8c
fix(OFJAVA-033): 41 review findings across 30 files
flamingo[bot] Sep 14, 2026
effc826
fix(OFJAVA-033): 41 review findings across 30 files
flamingo[bot] Sep 14, 2026
a86a51a
fix(OFJAVA-033): 41 review findings across 30 files
flamingo[bot] Sep 14, 2026
1ba1b52
fix(OFJAVA-033): 41 review findings across 30 files
flamingo[bot] Sep 14, 2026
f39fc0a
fix(OFJAVA-033): 41 review findings across 30 files
flamingo[bot] Sep 14, 2026
ce3f552
fix(OFJAVA-033): 41 review findings across 30 files
flamingo[bot] Sep 14, 2026
e2394a0
fix(OFJAVA-033): 41 review findings across 30 files
flamingo[bot] Sep 14, 2026
278a218
fix(OFJAVA-033): 41 review findings across 30 files
flamingo[bot] Sep 14, 2026
cd33a8b
fix(OFJAVA-033): 41 review findings across 30 files
flamingo[bot] Sep 14, 2026
e38fa9d
fix(OFJAVA-033): 41 review findings across 30 files
flamingo[bot] Sep 14, 2026
5582220
fix(OFJAVA-033): 41 review findings across 30 files
flamingo[bot] Sep 14, 2026
6983c76
fix(OFJAVA-033): 41 review findings across 30 files
flamingo[bot] Sep 14, 2026
f219594
fix(OFJAVA-033): 41 review findings across 30 files
flamingo[bot] Sep 14, 2026
9618985
fix(OFJAVA-033): 41 review findings across 30 files
flamingo[bot] Sep 14, 2026
17796ba
fix(OFJAVA-033): 41 review findings across 30 files
flamingo[bot] Sep 14, 2026
8b28bdc
fix(OFJAVA-033): 41 review findings across 30 files
flamingo[bot] Sep 14, 2026
8d033ae
fix(OFJAVA-033): 41 review findings across 30 files
flamingo[bot] Sep 14, 2026
eb8262e
fix(OFJAVA-033): 41 review findings across 30 files
flamingo[bot] Sep 14, 2026
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,7 +20,7 @@ public class OrganizationMapper {
*/
public Organization toEntity(CreateOrganizationRequest request) {
if (request == null) {
return null;
throw new IllegalArgumentException("request must not be null");
}

return Organization.builder()
Expand Down Expand Up @@ -52,6 +52,9 @@ private String generateOrganizationId() {
* Note: organizationId cannot be updated - it's immutable once created.
*/
public Organization updateEntity(Organization existing, UpdateOrganizationRequest request) {
if (existing == null) {
throw new IllegalArgumentException("existing must not be null");
}
if (request == null) {
return existing;
}
Expand Down Expand Up @@ -93,7 +96,7 @@ public Organization updateEntity(Organization existing, UpdateOrganizationReques
*/
public OrganizationResponse toResponse(Organization organization) {
if (organization == null) {
return null;
throw new IllegalArgumentException("organization must not be null");
}

return OrganizationResponse.builder()
Expand Down Expand Up @@ -121,7 +124,7 @@ public OrganizationResponse toResponse(Organization organization) {

private ContactInformation toContactInformationEntity(ContactInformationDto dto) {
if (dto == null) {
return null;
return ContactInformation.builder().build();
}

Address physicalAddress = toAddressEntity(dto.physicalAddress());
Expand All @@ -146,7 +149,7 @@ private ContactInformation toContactInformationEntity(ContactInformationDto dto)

private ContactPerson toContactPersonEntity(ContactPersonDto dto) {
if (dto == null) {
return null;
return ContactPerson.builder().build();
}

return ContactPerson.builder()
Expand Down Expand Up @@ -174,7 +177,7 @@ private Address toAddressEntity(AddressDto dto) {

private ContactInformationDto toContactInformationDto(ContactInformation entity) {
if (entity == null) {
return null;
return ContactInformationDto.builder().build();
}

return ContactInformationDto.builder()
Expand All @@ -189,7 +192,7 @@ private ContactInformationDto toContactInformationDto(ContactInformation entity)

private ContactPersonDto toContactPersonDto(ContactPerson entity) {
if (entity == null) {
return null;
return ContactPersonDto.builder().build();
}

return ContactPersonDto.builder()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
package com.openframe.api.dto;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.util.List;

// Defaults are collapsed server-side: every group exactly once β€” the client never re-implements the defaulting rules.
@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class NotificationSettingsView {

private final boolean enabled;
private final List<NotificationTypeSetting> typeSettings;
private boolean enabled;
private List<NotificationTypeSetting> typeSettings;
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
package com.openframe.api.dto.force.response;

import lombok.Data;
import lombok.Builder;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;

import java.util.List;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ForceToolAgentUpdateResponse {

private List<ForceToolAgentUpdateResponseItem> items;

}

Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
package com.openframe.api.dto.force.response;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ForceToolAgentUpdateResponseItem {

private String machineId;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,27 @@

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.time.Instant;

@Getter
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
class ChocoEntry {

private final String id;
private final String title;
private final String summary;
private final String description;
private final String version;
private final Integer downloadCount;
private final String iconUrl;
private final String projectUrl;
private final String tags;
private final Instant published;
private final Boolean prerelease;
private String id;
private String title;
private String summary;
private String description;
private String version;
private Integer downloadCount;
private String iconUrl;
private String projectUrl;
private String tags;
private Instant published;
private Boolean prerelease;
}

Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
Expand Down Expand Up @@ -60,12 +62,16 @@ public class SsoJoinController {
private final SsoOidcUserService ssoOidcUserService;
private final TenantService tenantService;

public record JoinPendingResponse(String email,
String firstName,
String lastName,
String provider,
String tenantName,
List<String> roles) {}
@Getter
@AllArgsConstructor
public static class JoinPendingResponse {
private final String email;
private final String firstName;
private final String lastName;
private final String provider;
private final String tenantName;
private final List<String> roles;
}

@GetMapping("/pending")
public JoinPendingResponse pending(Authentication authentication, HttpServletRequest request) {
Expand Down Expand Up @@ -106,9 +112,7 @@ public void complete(@RequestParam(value = "agreeTerms", defaultValue = "false")
HttpServletRequest request,
HttpServletResponse response) throws IOException {
OidcUser user = requireSessionOidcUser(authentication);
if (!agreeTerms) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "terms_not_accepted");
}
requireTermsAgreed(agreeTerms);
String[] names = OidcUserUtils.resolveNames(user);
String provider = SsoAuthentication.registrationId(authentication);

Expand Down Expand Up @@ -158,6 +162,12 @@ private void requireInviteBoundToSession(HttpServletRequest request, String invi
}
}

private void requireTermsAgreed(boolean agreeTerms) {
if (!agreeTerms) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "terms_not_accepted");
}
}

private String tenantName(String tenantId) {
return tenantService.findById(tenantId).map(Tenant::getName).orElse("");
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
package com.openframe.authz.security;

import lombok.AllArgsConstructor;
import lombok.Getter;

/**
* Flow cookie for the email-less SSO login: the user picked a provider on the login page without
* entering an email, so the tenant is unknown until the provider's callback identifies them.
*/
public record SsoLoginCookiePayload(
String s,
String provider,
String redirectTo,
boolean authMobile,
long iat,
long exp
) implements SsoCookiePayload {
@Getter
@AllArgsConstructor
public class SsoLoginCookiePayload implements SsoCookiePayload {
private final String s;
private final String provider;
private final String redirectTo;
private final boolean authMobile;
private final long iat;
private final long exp;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.openframe.authz.service.sso;

/**
* Thrown when a signup ticket cannot be found or has expired in Redis, indicating the
* pending signup session is no longer valid and the user must restart the flow.
*/
public class SignupTicketExpiredException extends RuntimeException {

public SignupTicketExpiredException(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import com.fasterxml.jackson.databind.ObjectMapper;
import com.openframe.data.redis.OpenframeRedisKeyBuilder;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
Expand Down Expand Up @@ -32,14 +34,17 @@ public class SignupTicketService {
private final OpenframeRedisKeyBuilder keyBuilder;
private final ObjectMapper objectMapper;

public record SignupTicketPayload(String email,
String firstName,
String lastName,
String provider,
boolean emailVerified,
String subject,
String userId,
String tenantId) {
@Getter
@AllArgsConstructor
public static class SignupTicketPayload {
private final String email;
private final String firstName;
private final String lastName;
private final String provider;
private final boolean emailVerified;
private final String subject;
private final String userId;
private final String tenantId;

public boolean bound() {
return userId != null && tenantId != null;
Expand All @@ -65,13 +70,13 @@ public void bind(String ticket, String userId, String tenantId) {
String k = key(ticket);
Optional<SignupTicketPayload> current = decode(redisTemplate.opsForValue().get(k));
if (current.isEmpty()) {
throw new IllegalStateException("Signup session expired. Please sign in again.");
throw new SignupTicketExpiredException("Signup session expired. Please sign in again.");
}
SignupTicketPayload p = current.get();
Long remaining = redisTemplate.getExpire(k);
Duration ttl = remaining != null && remaining > 0 ? Duration.ofSeconds(remaining) : TTL;
write(ticket, new SignupTicketPayload(p.email(), p.firstName(), p.lastName(), p.provider(),
p.emailVerified(), p.subject(), userId, tenantId), ttl);
write(ticket, new SignupTicketPayload(p.getEmail(), p.getFirstName(), p.getLastName(), p.getProvider(),
p.isEmailVerified(), p.getSubject(), userId, tenantId), ttl);
}

/** Atomic single use β€” the token mint, and only it, calls this. */
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
package com.openframe.client.dto;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AgentTokenResponse {
private String accessToken;
private String refreshToken;
private String tokenType;
private long expiresIn;
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
package com.openframe.client.dto.metrics;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.time.Instant;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class MetricsMessage {
private String machineId;
private double cpu;
private double memory;
private Instant timestamp;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,20 @@

import java.time.Duration;
import java.time.Instant;
import java.util.Optional;

@UtilityClass
public final class ScheduleRecurrence {

public static Instant nextRunAfter(Instant currentNextRun, Long repeatSeconds, Instant now) {
public static Optional<Instant> nextRunAfter(Instant currentNextRun, Long repeatSeconds, Instant now) {
if (repeatSeconds == null || repeatSeconds <= 0) {
return null;
return Optional.empty();
}
Duration step = Duration.ofSeconds(repeatSeconds);
Instant next = currentNextRun != null ? currentNextRun : now;
while (!next.isAfter(now)) {
next = next.plus(step);
}
return next;
return Optional.of(next);
}
}
Loading