Policies: scope to the owning team; editing restricted to team leaders (#6632)

This commit is contained in:
Reece Browne
2026-06-11 23:21:48 +01:00
committed by GitHub
parent ddf10f0aaf
commit e88d22d2fc
13 changed files with 455 additions and 48 deletions
@@ -0,0 +1,32 @@
package stirling.software.saas.security;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;
import lombok.RequiredArgsConstructor;
import stirling.software.proprietary.policy.config.PolicyManagementAuthority;
/**
* SaaS policy context: only the LEADER of the current user's team may edit policies, and every user
* is scoped to their own team. Replaces the self-hosted global-admin check, which is meaningless on
* SaaS (a single global admin exists for the whole deployment, never per-org) — and the admin gets
* no cross-team escape: scoping binds them like everyone else.
*/
@Component
@Profile("saas")
@RequiredArgsConstructor
public class TeamLeaderPolicyManagementAuthority implements PolicyManagementAuthority {
private final TeamSecurityExpressions teamSecurity;
@Override
public boolean canEditPolicies() {
return teamSecurity.isCurrentUserTeamLeader();
}
@Override
public Long currentUserTeamId() {
return teamSecurity.currentUserTeamId();
}
}
@@ -44,6 +44,27 @@ public class TeamSecurityExpressions {
.orElse(false);
}
/** Whether the current authenticated user is a {@code LEADER} of their own team. */
public boolean isCurrentUserTeamLeader() {
User currentUser = getCurrentUser();
if (currentUser == null || currentUser.getTeam() == null) {
return false;
}
return membershipRepository
.findByTeamIdAndUserId(currentUser.getTeam().getId(), currentUser.getId())
.map(membership -> membership.getRole() == TeamRole.LEADER)
.orElse(false);
}
/** The current authenticated user's team id, or {@code null} if unauthenticated / teamless. */
public Long currentUserTeamId() {
User currentUser = getCurrentUser();
if (currentUser == null || currentUser.getTeam() == null) {
return null;
}
return currentUser.getTeam().getId();
}
/** Whether the current authenticated user is any kind of member of the given team. */
public boolean isTeamMember(Long teamId) {
User currentUser = getCurrentUser();