mirror of
https://github.com/Frooodle/Stirling-PDF.git
synced 2026-04-22 23:08:53 +02:00
Invite-link-issues (#5983)
This commit is contained in:
@@ -26,6 +26,7 @@ import stirling.software.proprietary.security.service.EmailService;
|
||||
import stirling.software.proprietary.security.service.SaveUserRequest;
|
||||
import stirling.software.proprietary.security.service.TeamService;
|
||||
import stirling.software.proprietary.security.service.UserService;
|
||||
import stirling.software.proprietary.service.UserLicenseSettingsService;
|
||||
|
||||
@InviteApi
|
||||
@Slf4j
|
||||
@@ -37,6 +38,7 @@ public class InviteLinkController {
|
||||
private final UserService userService;
|
||||
private final ApplicationProperties applicationProperties;
|
||||
private final Optional<EmailService> emailService;
|
||||
private final UserLicenseSettingsService userLicenseSettingsService;
|
||||
|
||||
/**
|
||||
* Generate a new invite link (admin only)
|
||||
@@ -58,6 +60,7 @@ public class InviteLinkController {
|
||||
@RequestParam(name = "teamId", required = false) Long teamId,
|
||||
@RequestParam(name = "expiryHours", required = false) Integer expiryHours,
|
||||
@RequestParam(name = "sendEmail", defaultValue = "false") boolean sendEmail,
|
||||
@RequestParam(name = "frontendBaseUrl", required = false) String frontendBaseUrl,
|
||||
Principal principal,
|
||||
HttpServletRequest request) {
|
||||
|
||||
@@ -95,10 +98,6 @@ public class InviteLinkController {
|
||||
+ " address"));
|
||||
}
|
||||
|
||||
// If sendEmail is requested but no email provided, reject
|
||||
if (sendEmail) {
|
||||
// Email will be sent
|
||||
}
|
||||
} else {
|
||||
// No email provided - this is a general invite link
|
||||
email = null; // Ensure it's null, not empty string
|
||||
@@ -114,7 +113,7 @@ public class InviteLinkController {
|
||||
if (applicationProperties.getPremium().isEnabled()) {
|
||||
long currentUserCount = userService.getTotalUsersCount();
|
||||
long activeInvites = inviteTokenRepository.countActiveInvites(LocalDateTime.now());
|
||||
int maxUsers = applicationProperties.getPremium().getMaxUsers();
|
||||
int maxUsers = userLicenseSettingsService.calculateMaxAllowedUsers();
|
||||
|
||||
if (currentUserCount + activeInvites >= maxUsers) {
|
||||
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
|
||||
@@ -180,19 +179,18 @@ public class InviteLinkController {
|
||||
|
||||
inviteTokenRepository.save(inviteToken);
|
||||
|
||||
// Build invite URL
|
||||
// Use configured frontend URL if available, otherwise fall back to backend URL
|
||||
// Build invite URL: system.frontendUrl → caller's frontendBaseUrl → system.backendUrl →
|
||||
// request URL
|
||||
String baseUrl;
|
||||
String configuredFrontendUrl = applicationProperties.getSystem().getFrontendUrl();
|
||||
String configuredBackendUrl = applicationProperties.getSystem().getBackendUrl();
|
||||
if (configuredFrontendUrl != null && !configuredFrontendUrl.trim().isEmpty()) {
|
||||
// Use configured frontend URL (remove trailing slash if present)
|
||||
baseUrl =
|
||||
configuredFrontendUrl.endsWith("/")
|
||||
? configuredFrontendUrl.substring(
|
||||
0, configuredFrontendUrl.length() - 1)
|
||||
: configuredFrontendUrl;
|
||||
baseUrl = configuredFrontendUrl.trim();
|
||||
} else if (frontendBaseUrl != null && !frontendBaseUrl.trim().isEmpty()) {
|
||||
baseUrl = frontendBaseUrl.trim();
|
||||
} else if (configuredBackendUrl != null && !configuredBackendUrl.trim().isEmpty()) {
|
||||
baseUrl = configuredBackendUrl.trim();
|
||||
} else {
|
||||
// Fall back to backend URL from request
|
||||
baseUrl =
|
||||
request.getScheme()
|
||||
+ "://"
|
||||
@@ -201,7 +199,10 @@ public class InviteLinkController {
|
||||
? ":" + request.getServerPort()
|
||||
: "");
|
||||
}
|
||||
String inviteUrl = baseUrl + "/invite?token=" + token;
|
||||
if (baseUrl.endsWith("/")) {
|
||||
baseUrl = baseUrl.substring(0, baseUrl.length() - 1);
|
||||
}
|
||||
String inviteUrl = baseUrl + "/invite/" + token;
|
||||
|
||||
log.info("Generated invite link for {} by {}", email, principal.getName());
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ import stirling.software.proprietary.security.repository.TeamRepository;
|
||||
import stirling.software.proprietary.security.service.EmailService;
|
||||
import stirling.software.proprietary.security.service.TeamService;
|
||||
import stirling.software.proprietary.security.service.UserService;
|
||||
import stirling.software.proprietary.service.UserLicenseSettingsService;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class InviteLinkControllerTest {
|
||||
@@ -39,6 +40,7 @@ class InviteLinkControllerTest {
|
||||
@Mock private TeamRepository teamRepository;
|
||||
@Mock private UserService userService;
|
||||
@Mock private EmailService emailService;
|
||||
@Mock private UserLicenseSettingsService userLicenseSettingsService;
|
||||
|
||||
private ApplicationProperties applicationProperties;
|
||||
private MockMvc mockMvc;
|
||||
@@ -59,7 +61,8 @@ class InviteLinkControllerTest {
|
||||
teamRepository,
|
||||
userService,
|
||||
applicationProperties,
|
||||
Optional.of(emailService));
|
||||
Optional.of(emailService),
|
||||
userLicenseSettingsService);
|
||||
mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
|
||||
}
|
||||
|
||||
@@ -89,9 +92,9 @@ class InviteLinkControllerTest {
|
||||
@Test
|
||||
void generateInviteLinkBlocksOnLicenseLimit() throws Exception {
|
||||
applicationProperties.getPremium().setEnabled(true);
|
||||
applicationProperties.getPremium().setMaxUsers(1);
|
||||
when(userService.getTotalUsersCount()).thenReturn(1L);
|
||||
when(inviteTokenRepository.countActiveInvites(any(LocalDateTime.class))).thenReturn(0L);
|
||||
when(userLicenseSettingsService.calculateMaxAllowedUsers()).thenReturn(1);
|
||||
|
||||
mockMvc.perform(
|
||||
post("/api/v1/invite/generate")
|
||||
@@ -101,6 +104,29 @@ class InviteLinkControllerTest {
|
||||
.andExpect(jsonPath("$.error").value(startsWith("License limit reached")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateInviteLinkAllowedOnServerLicense() throws Exception {
|
||||
// SERVER license has raw maxUsers=0, but calculateMaxAllowedUsers() returns
|
||||
// Integer.MAX_VALUE
|
||||
applicationProperties.getPremium().setEnabled(true);
|
||||
when(userService.getTotalUsersCount()).thenReturn(3L);
|
||||
when(inviteTokenRepository.countActiveInvites(any(LocalDateTime.class))).thenReturn(0L);
|
||||
when(userLicenseSettingsService.calculateMaxAllowedUsers()).thenReturn(Integer.MAX_VALUE);
|
||||
when(userService.usernameExistsIgnoreCase("new@ex.com")).thenReturn(false);
|
||||
when(inviteTokenRepository.findByEmail("new@ex.com")).thenReturn(Optional.empty());
|
||||
Team defaultTeam = new Team();
|
||||
defaultTeam.setId(1L);
|
||||
defaultTeam.setName(TeamService.DEFAULT_TEAM_NAME);
|
||||
when(teamRepository.findByName(TeamService.DEFAULT_TEAM_NAME))
|
||||
.thenReturn(Optional.of(defaultTeam));
|
||||
|
||||
mockMvc.perform(
|
||||
post("/api/v1/invite/generate")
|
||||
.principal(adminPrincipal)
|
||||
.param("email", "new@ex.com"))
|
||||
.andExpect(status().isOk());
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateInviteLinkBuildsFrontendUrl() throws Exception {
|
||||
Team defaultTeam = new Team();
|
||||
@@ -118,7 +144,7 @@ class InviteLinkControllerTest {
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(
|
||||
jsonPath("$.inviteUrl")
|
||||
.value(startsWith("https://frontend.example.com/invite?token=")))
|
||||
.value(startsWith("https://frontend.example.com/invite/")))
|
||||
.andExpect(jsonPath("$.email").value("new@example.com"));
|
||||
|
||||
verify(inviteTokenRepository).save(any());
|
||||
|
||||
Reference in New Issue
Block a user