mirror of
https://github.com/iflytek/skillhub.git
synced 2026-09-09 22:31:14 +00:00
Merge remote-tracking branch 'origin/main' into feature/project-fixbug
This commit is contained in:
commit
a4c0ea60a5
5 changed files with 218 additions and 0 deletions
|
|
@ -86,4 +86,18 @@ class AdminSkillControllerTest {
|
|||
.andExpect(jsonPath("$.data.action").value("YANK"))
|
||||
.andExpect(jsonPath("$.data.status").value("YANKED"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void hideSkill_withUserAdminRole_returns403() throws Exception {
|
||||
PlatformPrincipal principal = new PlatformPrincipal("admin", "admin", "a@example.com", "", "github", Set.of("USER_ADMIN"));
|
||||
var auth = new UsernamePasswordAuthenticationToken(principal, null, List.of(new SimpleGrantedAuthority("ROLE_USER_ADMIN")));
|
||||
|
||||
mockMvc.perform(post("/api/v1/admin/skills/10/hide")
|
||||
.with(authentication(auth))
|
||||
.with(csrf())
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("{\"reason\":\"policy\"}"))
|
||||
.andExpect(status().isForbidden())
|
||||
.andExpect(jsonPath("$.code").value(403));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -112,6 +112,22 @@ class AdminSkillReportControllerTest {
|
|||
.andExpect(jsonPath("$.data.status").value("RESOLVED"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void listReports_withAuditorRole_returns403() throws Exception {
|
||||
PlatformPrincipal principal = new PlatformPrincipal(
|
||||
"auditor", "auditor", "auditor@example.com", "", "github", Set.of("AUDITOR")
|
||||
);
|
||||
var auth = new UsernamePasswordAuthenticationToken(
|
||||
principal, null, List.of(new SimpleGrantedAuthority("ROLE_AUDITOR"))
|
||||
);
|
||||
|
||||
mockMvc.perform(get("/api/v1/admin/skill-reports")
|
||||
.param("status", "PENDING")
|
||||
.with(authentication(auth)))
|
||||
.andExpect(status().isForbidden())
|
||||
.andExpect(jsonPath("$.code").value(403));
|
||||
}
|
||||
|
||||
private UsernamePasswordAuthenticationToken adminAuth() {
|
||||
PlatformPrincipal principal = new PlatformPrincipal(
|
||||
"admin", "admin", "admin@example.com", "", "github", Set.of("SKILL_ADMIN")
|
||||
|
|
|
|||
|
|
@ -8,6 +8,9 @@ import static org.mockito.Mockito.verify;
|
|||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.iflytek.skillhub.auth.entity.IdentityBinding;
|
||||
import com.iflytek.skillhub.auth.entity.Role;
|
||||
import com.iflytek.skillhub.auth.entity.UserRoleBinding;
|
||||
import com.iflytek.skillhub.auth.oauth.AccountDisabledException;
|
||||
import com.iflytek.skillhub.auth.oauth.OAuthClaims;
|
||||
import com.iflytek.skillhub.auth.oauth.AccountPendingException;
|
||||
import com.iflytek.skillhub.auth.rbac.PlatformPrincipal;
|
||||
|
|
@ -26,6 +29,7 @@ import org.junit.jupiter.api.extension.ExtendWith;
|
|||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class IdentityBindingServiceTest {
|
||||
|
|
@ -91,4 +95,88 @@ class IdentityBindingServiceTest {
|
|||
|
||||
verify(globalNamespaceMembershipService, never()).ensureMember(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void bindOrCreate_defaultsToUserRoleWhenNoBindingsExist() {
|
||||
OAuthClaims claims = new OAuthClaims(
|
||||
"github",
|
||||
"gh_1",
|
||||
"alice@example.com",
|
||||
true,
|
||||
"alice",
|
||||
Map.of()
|
||||
);
|
||||
when(bindingRepo.findByProviderCodeAndSubject("github", "gh_1")).thenReturn(Optional.empty());
|
||||
when(userRepo.save(any(UserAccount.class))).thenAnswer(invocation -> invocation.getArgument(0));
|
||||
when(roleBindingRepo.findByUserId(any())).thenReturn(List.of());
|
||||
|
||||
PlatformPrincipal principal = service.bindOrCreate(claims, UserStatus.ACTIVE);
|
||||
|
||||
assertThat(principal.platformRoles()).containsExactly("USER");
|
||||
}
|
||||
|
||||
@Test
|
||||
void bindOrCreate_existingDisabledUser_throwsAccountDisabled() {
|
||||
OAuthClaims claims = new OAuthClaims(
|
||||
"github",
|
||||
"gh_1",
|
||||
"alice@example.com",
|
||||
true,
|
||||
"alice",
|
||||
Map.of()
|
||||
);
|
||||
IdentityBinding binding = new IdentityBinding("usr_1", "github", "gh_1", "alice");
|
||||
UserAccount user = new UserAccount("usr_1", "alice", "alice@example.com", null);
|
||||
user.setStatus(UserStatus.DISABLED);
|
||||
|
||||
when(bindingRepo.findByProviderCodeAndSubject("github", "gh_1")).thenReturn(Optional.of(binding));
|
||||
when(userRepo.findById("usr_1")).thenReturn(Optional.of(user));
|
||||
when(userRepo.save(any(UserAccount.class))).thenAnswer(invocation -> invocation.getArgument(0));
|
||||
|
||||
assertThatThrownBy(() -> service.bindOrCreate(claims, UserStatus.ACTIVE))
|
||||
.isInstanceOf(AccountDisabledException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void bindOrCreate_returnsExplicitPlatformRolesWhenBindingsExist() {
|
||||
OAuthClaims claims = new OAuthClaims(
|
||||
"github",
|
||||
"gh_1",
|
||||
"alice@example.com",
|
||||
true,
|
||||
"alice",
|
||||
Map.of()
|
||||
);
|
||||
when(bindingRepo.findByProviderCodeAndSubject("github", "gh_1")).thenReturn(Optional.empty());
|
||||
when(userRepo.save(any(UserAccount.class))).thenAnswer(invocation -> invocation.getArgument(0));
|
||||
|
||||
Role role = new Role();
|
||||
ReflectionTestUtils.setField(role, "code", "AUDITOR");
|
||||
when(roleBindingRepo.findByUserId(any())).thenReturn(List.of(new UserRoleBinding("usr_1", role)));
|
||||
|
||||
PlatformPrincipal principal = service.bindOrCreate(claims, UserStatus.ACTIVE);
|
||||
|
||||
assertThat(principal.platformRoles()).containsExactly("AUDITOR");
|
||||
}
|
||||
|
||||
@Test
|
||||
void createPendingUserIfAbsent_existingDisabledBinding_throwsAccountDisabled() {
|
||||
OAuthClaims claims = new OAuthClaims(
|
||||
"github",
|
||||
"gh_1",
|
||||
"alice@example.com",
|
||||
true,
|
||||
"alice",
|
||||
Map.of()
|
||||
);
|
||||
IdentityBinding binding = new IdentityBinding("usr_1", "github", "gh_1", "alice");
|
||||
UserAccount user = new UserAccount("usr_1", "alice", "alice@example.com", null);
|
||||
user.setStatus(UserStatus.DISABLED);
|
||||
|
||||
when(bindingRepo.findByProviderCodeAndSubject("github", "gh_1")).thenReturn(Optional.of(binding));
|
||||
when(userRepo.findById("usr_1")).thenReturn(Optional.of(user));
|
||||
|
||||
assertThatThrownBy(() -> service.createPendingUserIfAbsent(claims))
|
||||
.isInstanceOf(AccountDisabledException.class);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -150,6 +150,49 @@ class LocalAuthServiceTest {
|
|||
.hasMessageContaining("error.auth.local.accountDisabled");
|
||||
}
|
||||
|
||||
@Test
|
||||
void login_withPendingAccount_fails() {
|
||||
LocalCredential credential = new LocalCredential("usr_1", "alice", "encoded");
|
||||
UserAccount user = new UserAccount("usr_1", "alice", "alice@example.com", null);
|
||||
user.setStatus(UserStatus.PENDING);
|
||||
|
||||
given(credentialRepository.findByUsernameIgnoreCase("alice")).willReturn(Optional.of(credential));
|
||||
given(userAccountRepository.findById("usr_1")).willReturn(Optional.of(user));
|
||||
|
||||
assertThatThrownBy(() -> service.login("alice", "Abcd123!"))
|
||||
.isInstanceOf(AuthFlowException.class)
|
||||
.hasMessageContaining("error.auth.local.accountPending");
|
||||
}
|
||||
|
||||
@Test
|
||||
void login_withMergedAccount_fails() {
|
||||
LocalCredential credential = new LocalCredential("usr_1", "alice", "encoded");
|
||||
UserAccount user = new UserAccount("usr_1", "alice", "alice@example.com", null);
|
||||
user.setStatus(UserStatus.MERGED);
|
||||
|
||||
given(credentialRepository.findByUsernameIgnoreCase("alice")).willReturn(Optional.of(credential));
|
||||
given(userAccountRepository.findById("usr_1")).willReturn(Optional.of(user));
|
||||
|
||||
assertThatThrownBy(() -> service.login("alice", "Abcd123!"))
|
||||
.isInstanceOf(AuthFlowException.class)
|
||||
.hasMessageContaining("error.auth.local.accountMerged");
|
||||
}
|
||||
|
||||
@Test
|
||||
void login_withoutExplicitRoles_defaultsToUser() {
|
||||
LocalCredential credential = new LocalCredential("usr_1", "alice", "encoded");
|
||||
UserAccount user = new UserAccount("usr_1", "alice", "alice@example.com", null);
|
||||
|
||||
given(credentialRepository.findByUsernameIgnoreCase("alice")).willReturn(Optional.of(credential));
|
||||
given(userAccountRepository.findById("usr_1")).willReturn(Optional.of(user));
|
||||
given(passwordEncoder.matches("Abcd123!", "encoded")).willReturn(true);
|
||||
given(userRoleBindingRepository.findByUserId("usr_1")).willReturn(List.of());
|
||||
|
||||
var principal = service.login("alice", "Abcd123!");
|
||||
|
||||
assertThat(principal.platformRoles()).containsExactly("USER");
|
||||
}
|
||||
|
||||
@Test
|
||||
void register_rejectsInvalidEmailFormat() {
|
||||
given(credentialRepository.existsByUsernameIgnoreCase("alice")).willReturn(false);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,57 @@
|
|||
package com.iflytek.skillhub.domain.namespace;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class NamespaceAccessPolicyTest {
|
||||
|
||||
private final NamespaceAccessPolicy policy = new NamespaceAccessPolicy();
|
||||
|
||||
@Test
|
||||
void globalNamespaceIsImmutable() {
|
||||
Namespace namespace = new Namespace("global", "Global", "owner");
|
||||
namespace.setType(NamespaceType.GLOBAL);
|
||||
|
||||
assertThat(policy.isImmutable(namespace)).isTrue();
|
||||
assertThat(policy.canMutateSettings(namespace)).isFalse();
|
||||
assertThat(policy.canManageMembers(namespace)).isFalse();
|
||||
assertThat(policy.canTransferOwnership(namespace)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void activeTeamNamespaceAllowsAdminAndOwnerToFreezeButNotMember() {
|
||||
Namespace namespace = new Namespace("team-a", "Team A", "owner");
|
||||
namespace.setType(NamespaceType.TEAM);
|
||||
namespace.setStatus(NamespaceStatus.ACTIVE);
|
||||
|
||||
assertThat(policy.canFreeze(namespace, NamespaceRole.OWNER)).isTrue();
|
||||
assertThat(policy.canFreeze(namespace, NamespaceRole.ADMIN)).isTrue();
|
||||
assertThat(policy.canFreeze(namespace, NamespaceRole.MEMBER)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void frozenTeamNamespaceAllowsAdminAndOwnerToUnfreezeButNotMember() {
|
||||
Namespace namespace = new Namespace("team-a", "Team A", "owner");
|
||||
namespace.setType(NamespaceType.TEAM);
|
||||
namespace.setStatus(NamespaceStatus.FROZEN);
|
||||
|
||||
assertThat(policy.canUnfreeze(namespace, NamespaceRole.OWNER)).isTrue();
|
||||
assertThat(policy.canUnfreeze(namespace, NamespaceRole.ADMIN)).isTrue();
|
||||
assertThat(policy.canUnfreeze(namespace, NamespaceRole.MEMBER)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void archiveAndRestoreAreOwnerOnly() {
|
||||
Namespace namespace = new Namespace("team-a", "Team A", "owner");
|
||||
namespace.setType(NamespaceType.TEAM);
|
||||
namespace.setStatus(NamespaceStatus.ACTIVE);
|
||||
|
||||
assertThat(policy.canArchive(namespace, NamespaceRole.OWNER)).isTrue();
|
||||
assertThat(policy.canArchive(namespace, NamespaceRole.ADMIN)).isFalse();
|
||||
|
||||
namespace.setStatus(NamespaceStatus.ARCHIVED);
|
||||
assertThat(policy.canRestore(namespace, NamespaceRole.OWNER)).isTrue();
|
||||
assertThat(policy.canRestore(namespace, NamespaceRole.ADMIN)).isFalse();
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue