mirror of
https://github.com/HangarMC/Hangar.git
synced 2024-11-21 01:21:54 +08:00
cleanup urls, nuke hangar user principal
This commit is contained in:
parent
ac8fa50fd6
commit
d26589f1e1
@ -5,4 +5,4 @@
|
||||
<file url="file://$PROJECT_DIR$/backend/src/main/resources" charset="UTF-8" />
|
||||
<file url="PROJECT" charset="UTF-8" />
|
||||
</component>
|
||||
</project>
|
||||
</project>
|
||||
|
@ -52,7 +52,7 @@ public class LoginController extends HangarComponent {
|
||||
this.config.checkDev();
|
||||
|
||||
final UserTable fakeUser = this.authenticationService.loginAsFakeUser();
|
||||
this.tokenService.issueRefreshToken(fakeUser, this.response);
|
||||
this.tokenService.issueRefreshToken(fakeUser.getUserId(), this.response);
|
||||
return this.addBaseAndRedirect(this.cutoffAbsoluteUrls(returnUrl));
|
||||
} else {
|
||||
this.response.addCookie(new Cookie("url", this.cutoffAbsoluteUrls(returnUrl)));
|
||||
@ -71,7 +71,7 @@ public class LoginController extends HangarComponent {
|
||||
if (!this.validationService.isValidUsername(user.getName())) {
|
||||
throw new HangarApiException("nav.user.error.invalidUsername");
|
||||
}
|
||||
this.tokenService.issueRefreshToken(user, this.response);
|
||||
this.tokenService.issueRefreshToken(user.getUserId(), this.response);
|
||||
return this.addBaseAndRedirect(this.cutoffAbsoluteUrls(url));
|
||||
}
|
||||
|
||||
|
@ -11,7 +11,6 @@ import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
@ -20,7 +19,7 @@ import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
|
||||
@Controller
|
||||
@RateLimit(path = "auth")
|
||||
@RequestMapping(path = "/api/internal", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
@RequestMapping(path = "/api/internal/auth", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public class AuthController extends HangarComponent {
|
||||
|
||||
private final HangarWebAuthnAuthenticatorService hangarWebAuthnAuthenticatorService;
|
||||
@ -29,7 +28,7 @@ public class AuthController extends HangarComponent {
|
||||
this.hangarWebAuthnAuthenticatorService = hangarWebAuthnAuthenticatorService;
|
||||
}
|
||||
|
||||
@PostMapping("/auth/signup")
|
||||
@PostMapping("/signup")
|
||||
public void signup() {
|
||||
|
||||
}
|
||||
|
@ -3,19 +3,27 @@ package io.papermc.hangar.security.authentication;
|
||||
import io.papermc.hangar.model.common.Permission;
|
||||
import io.papermc.hangar.model.db.projects.ProjectOwner;
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
|
||||
public class HangarPrincipal implements ProjectOwner, Serializable {
|
||||
public class HangarPrincipal implements ProjectOwner, UserDetails, Serializable {
|
||||
|
||||
private final long id;
|
||||
private final String name;
|
||||
private final boolean locked;
|
||||
private final Permission globalPermissions;
|
||||
private int aal = -1;
|
||||
private final String password;
|
||||
|
||||
public HangarPrincipal(final long id, final String name, final boolean locked, final Permission globalPermissions) {
|
||||
public HangarPrincipal(final long id, final String name, final boolean locked, final Permission globalPermissions, final String password) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.locked = locked;
|
||||
this.globalPermissions = globalPermissions;
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -45,6 +53,14 @@ public class HangarPrincipal implements ProjectOwner, Serializable {
|
||||
return this.globalPermissions.intersect(this.getPossiblePermissions());
|
||||
}
|
||||
|
||||
public int getAal() {
|
||||
return this.aal;
|
||||
}
|
||||
|
||||
public void setAal(final int aal) {
|
||||
this.aal = aal;
|
||||
}
|
||||
|
||||
public final boolean isAllowedGlobal(final Permission requiredPermission) {
|
||||
return this.isAllowed(requiredPermission, this.globalPermissions);
|
||||
}
|
||||
@ -57,6 +73,41 @@ public class HangarPrincipal implements ProjectOwner, Serializable {
|
||||
return this.getPossiblePermissions().has(requiredPermission.intersect(currentPermission));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends GrantedAuthority> getAuthorities() {
|
||||
return List.of(new SimpleGrantedAuthority("dum"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return this.password;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return this.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAccountNonExpired() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAccountNonLocked() {
|
||||
return !this.isLocked();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCredentialsNonExpired() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return !this.isLocked();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "HangarPrincipal{" +
|
||||
@ -64,6 +115,7 @@ public class HangarPrincipal implements ProjectOwner, Serializable {
|
||||
", name='" + this.name + '\'' +
|
||||
", locked=" + this.locked +
|
||||
", globalPermissions=" + this.globalPermissions +
|
||||
", aal=" + this.aal +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ public class HangarApiPrincipal extends HangarPrincipal {
|
||||
private final ApiKeyTable apiKeyTable;
|
||||
|
||||
public HangarApiPrincipal(final long id, final String name, final boolean locked, final Permission globalPermissions, final ApiKeyTable apiKeyTable) {
|
||||
super(id, name, locked, globalPermissions);
|
||||
super(id, name, locked, globalPermissions, null);
|
||||
this.apiKeyTable = apiKeyTable;
|
||||
}
|
||||
|
||||
|
@ -1,77 +0,0 @@
|
||||
package io.papermc.hangar.security.authentication.user;
|
||||
|
||||
import io.papermc.hangar.model.common.Permission;
|
||||
import io.papermc.hangar.model.db.UserTable;
|
||||
import io.papermc.hangar.security.authentication.HangarPrincipal;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
|
||||
public class HangarUserPrincipal extends HangarPrincipal implements UserDetails {
|
||||
|
||||
private final UserTable userTable;
|
||||
private final String password;
|
||||
private int aal = -1;
|
||||
|
||||
public HangarUserPrincipal(final UserTable userTable, final String password, final Permission globalPermissions) {
|
||||
super(userTable.getUserId(), userTable.getName(), userTable.isLocked(), globalPermissions);
|
||||
this.userTable = userTable;
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public UserTable getUserTable() {
|
||||
return this.userTable;
|
||||
}
|
||||
|
||||
public int getAal() {
|
||||
return this.aal;
|
||||
}
|
||||
|
||||
public void setAal(final int aal) {
|
||||
this.aal = aal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "HangarUserPrincipal{" +
|
||||
"userTable=" + this.userTable +
|
||||
"} " + super.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends GrantedAuthority> getAuthorities() {
|
||||
return List.of(new SimpleGrantedAuthority("dum"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return this.password;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return this.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAccountNonExpired() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAccountNonLocked() {
|
||||
return !this.isLocked();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCredentialsNonExpired() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return !this.isLocked();
|
||||
}
|
||||
}
|
@ -93,7 +93,7 @@ public class SecurityConfig {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public DaoAuthenticationProvider daoAuthenticationProvider(final UserDetailsService userDetailsService){
|
||||
public DaoAuthenticationProvider daoAuthenticationProvider(final UserDetailsService userDetailsService) {
|
||||
final DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
|
||||
daoAuthenticationProvider.setUserDetailsService(userDetailsService);
|
||||
daoAuthenticationProvider.setPasswordEncoder(this.passwordEncoder());
|
||||
|
@ -27,7 +27,7 @@ import com.webauthn4j.springframework.security.options.RpIdProvider;
|
||||
import com.webauthn4j.springframework.security.options.RpIdProviderImpl;
|
||||
import com.webauthn4j.springframework.security.server.ServerPropertyProvider;
|
||||
import io.papermc.hangar.security.authentication.HangarAuthenticationEntryPoint;
|
||||
import io.papermc.hangar.security.authentication.user.HangarUserPrincipal;
|
||||
import io.papermc.hangar.security.authentication.HangarPrincipal;
|
||||
import io.papermc.hangar.service.TokenService;
|
||||
import io.papermc.hangar.service.internal.auth.HangarPublicKeyCredentialUserEntityProvider;
|
||||
import io.papermc.hangar.service.internal.auth.HangarUserDetailService;
|
||||
@ -51,20 +51,26 @@ public class WebAuthnConfig {
|
||||
this.entryPoint = entryPoint;
|
||||
}
|
||||
|
||||
// TODO do we need to save aal into jwt?
|
||||
// prolly update the jwp in response to login endpoint
|
||||
|
||||
// TODO need to figure out how to handle aal + api keys
|
||||
|
||||
public void configure(final HttpSecurity http, final AuthenticationManager authenticationManager) throws Exception {
|
||||
http.apply(WebAuthnLoginConfigurer.webAuthnLogin())
|
||||
.loginProcessingUrl("/api/internal/auth/login")
|
||||
.successHandler((request, response, authentication) -> {
|
||||
if (authentication instanceof final UsernamePasswordAuthenticationToken token) {
|
||||
if (token.getPrincipal() instanceof final HangarUserPrincipal principal) {
|
||||
this.tokenService.issueRefreshToken(principal.getUserTable(), response);
|
||||
principal.setAal(1); // check if email verified, if not, aal = 0
|
||||
if (token.getPrincipal() instanceof final HangarPrincipal principal) {
|
||||
this.tokenService.issueRefreshToken(principal.getUserId(), response);
|
||||
principal.setAal(1); // todo check if email verified, if not, aal = 0
|
||||
System.out.println("woooo 1");
|
||||
return;
|
||||
}
|
||||
} else if (authentication instanceof final WebAuthnAuthenticationToken token) {
|
||||
if (token.getPrincipal() instanceof final HangarUserPrincipal principal) {
|
||||
if (token.getPrincipal() instanceof final HangarPrincipal principal) {
|
||||
principal.setAal(2);
|
||||
System.out.println("woooo");
|
||||
System.out.println("woooo 2");
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -73,7 +79,7 @@ public class WebAuthnConfig {
|
||||
})
|
||||
.failureHandler(new AuthenticationEntryPointFailureHandler(this.entryPoint))
|
||||
.attestationOptionsEndpoint()
|
||||
.processingUrl("/api/internal/webauthn/attestation/options")
|
||||
.processingUrl("/api/internal/auth/webauthn/attestation/options")
|
||||
.attestation(AttestationConveyancePreference.NONE)
|
||||
.timeout(60000L)
|
||||
.rp()
|
||||
@ -90,7 +96,7 @@ public class WebAuthnConfig {
|
||||
.credProps(true)
|
||||
.and()
|
||||
.assertionOptionsEndpoint()
|
||||
.processingUrl("/api/internal/webauthn/assertion/options")
|
||||
.processingUrl("/api/internal/auth/webauthn/assertion/options")
|
||||
.timeout(60000L)
|
||||
.and().and()
|
||||
.authenticationManager(authenticationManager);
|
||||
|
@ -53,8 +53,8 @@ public class TokenService extends HangarComponent {
|
||||
return this.getVerifier().verify(token);
|
||||
}
|
||||
|
||||
public void issueRefreshToken(final UserTable userTable, final HttpServletResponse response) {
|
||||
final UserRefreshToken userRefreshToken = this.userRefreshTokenDAO.insert(new UserRefreshToken(userTable.getId(), UUID.randomUUID(), UUID.randomUUID()));
|
||||
public void issueRefreshToken(final long userId, final HttpServletResponse response) {
|
||||
final UserRefreshToken userRefreshToken = this.userRefreshTokenDAO.insert(new UserRefreshToken(userId, UUID.randomUUID(), UUID.randomUUID()));
|
||||
this.addCookie(SecurityConfig.REFRESH_COOKIE_NAME, userRefreshToken.getToken().toString(), this.config.security.refreshTokenExpiry().toSeconds(), true, response);
|
||||
}
|
||||
|
||||
@ -147,7 +147,7 @@ public class TokenService extends HangarComponent {
|
||||
}
|
||||
return new HangarApiPrincipal(userId, subject, locked, globalPermission, apiKeyTable);
|
||||
} else {
|
||||
return new HangarPrincipal(userId, subject, locked, globalPermission);
|
||||
return new HangarPrincipal(userId, subject, locked, globalPermission, null);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -59,7 +59,7 @@ public class FakeDataService extends HangarComponent {
|
||||
try {
|
||||
for (int udx = 0; udx < users; udx++) {
|
||||
final UserTable user = this.createUser();
|
||||
SecurityContextHolder.getContext().setAuthentication(HangarAuthenticationToken.createVerifiedToken(new HangarPrincipal(user.getUserId(), user.getName(), false, Permission.All), oldAuth.getCredentials()));
|
||||
SecurityContextHolder.getContext().setAuthentication(HangarAuthenticationToken.createVerifiedToken(new HangarPrincipal(user.getUserId(), user.getName(), false, Permission.All, null), oldAuth.getCredentials()));
|
||||
for (int pdx = 0; pdx < projectsPerUser; pdx++) {
|
||||
this.createProject(user.getUserId());
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ package io.papermc.hangar.service.internal.auth;
|
||||
|
||||
import com.webauthn4j.data.PublicKeyCredentialUserEntity;
|
||||
import com.webauthn4j.springframework.security.options.PublicKeyCredentialUserEntityProvider;
|
||||
import io.papermc.hangar.security.authentication.user.HangarUserPrincipal;
|
||||
import io.papermc.hangar.security.authentication.HangarPrincipal;
|
||||
import java.math.BigInteger;
|
||||
import org.springframework.security.core.Authentication;
|
||||
|
||||
@ -22,7 +22,7 @@ public class HangarPublicKeyCredentialUserEntityProvider implements PublicKeyCre
|
||||
System.out.println("load public key " + authentication);
|
||||
|
||||
final String username = authentication.getName();
|
||||
final HangarUserPrincipal principal = this.userDetailService.loadUserByUsername(username);
|
||||
final HangarPrincipal principal = this.userDetailService.loadUserByUsername(username);
|
||||
return new PublicKeyCredentialUserEntity(
|
||||
BigInteger.valueOf(principal.getUserId()).toByteArray(), // TODO this isn't a good id I guess?
|
||||
principal.getUsername(),
|
||||
|
@ -2,7 +2,7 @@ package io.papermc.hangar.service.internal.auth;
|
||||
|
||||
import io.papermc.hangar.model.common.Permission;
|
||||
import io.papermc.hangar.model.db.UserTable;
|
||||
import io.papermc.hangar.security.authentication.user.HangarUserPrincipal;
|
||||
import io.papermc.hangar.security.authentication.HangarPrincipal;
|
||||
import io.papermc.hangar.service.internal.users.UserService;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
@ -21,7 +21,7 @@ public class HangarUserDetailService implements UserDetailsService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public HangarUserPrincipal loadUserByUsername(final String username) throws UsernameNotFoundException {
|
||||
public HangarPrincipal loadUserByUsername(final String username) throws UsernameNotFoundException {
|
||||
if (username == null) {
|
||||
throw new UsernameNotFoundException("no user with null username");
|
||||
}
|
||||
@ -31,6 +31,7 @@ public class HangarUserDetailService implements UserDetailsService {
|
||||
throw new UsernameNotFoundException("no user in table");
|
||||
}
|
||||
// TODO store PW in db properly
|
||||
return new HangarUserPrincipal(userTable, this.passwordEncoder.encode("admin123"), Permission.ViewPublicInfo);
|
||||
// TODO load proper perms
|
||||
return new HangarPrincipal(userTable.getUserId(), userTable.getName(), userTable.isLocked(), Permission.ViewPublicInfo, this.passwordEncoder.encode("admin123"));
|
||||
}
|
||||
}
|
||||
|
@ -35,6 +35,7 @@ public class HangarWebAuthnAuthenticatorService implements WebAuthnAuthenticator
|
||||
@Override
|
||||
public WebAuthnAuthenticator loadAuthenticatorByCredentialId(final byte[] credentialId) throws CredentialIdNotFoundException {
|
||||
final WebAuthnAuthenticator auth = this.getByCredId(credentialId);
|
||||
// TODO we need to make sure to load the proper hangar user principal here
|
||||
System.out.println("loadAuthenticatorByCredentialId " + Arrays.toString(credentialId) + " " + auth.getUserPrincipal());
|
||||
return auth;
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ export function encodeBase64Url(arrayBuffer: ArrayBuffer) {
|
||||
}
|
||||
|
||||
export async function getAttestationOptions() {
|
||||
const response = await useInternalApi<PublicKeyCredentialCreationOptions>("webauthn/attestation/options");
|
||||
const response = await useInternalApi<PublicKeyCredentialCreationOptions>("auth/webauthn/attestation/options");
|
||||
console.log("response", response);
|
||||
|
||||
// TODO error handling
|
||||
@ -85,7 +85,7 @@ export async function getAttestationOptions() {
|
||||
}
|
||||
|
||||
export async function getAssertionOptions() {
|
||||
const response = await useInternalApi<PublicKeyCredentialRequestOptions>("webauthn/assertion/options");
|
||||
const response = await useInternalApi<PublicKeyCredentialRequestOptions>("auth/webauthn/assertion/options");
|
||||
console.log("response", response);
|
||||
|
||||
// TODO error handling
|
||||
|
@ -1,7 +1,7 @@
|
||||
<script lang="ts" setup>
|
||||
import { useHead } from "@vueuse/head";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import { ref } from "vue";
|
||||
import { computed, ref } from "vue";
|
||||
import { useSeo } from "~/composables/useSeo";
|
||||
import InputText from "~/lib/components/ui/InputText.vue";
|
||||
import Button from "~/lib/components/design/Button.vue";
|
||||
@ -9,11 +9,14 @@ import { useAxios } from "~/composables/useAxios";
|
||||
import { useAuth } from "~/composables/useAuth";
|
||||
import { useInternalApi } from "~/composables/useApi";
|
||||
import { encodeBase64Url, getAssertionOptions } from "~/composables/useWebAuthN";
|
||||
import { useAuthStore } from "~/store/auth";
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const authStore = useAuthStore();
|
||||
|
||||
const aal = ref(2);
|
||||
// todo hack for now, need to do this proper
|
||||
const aal = computed(() => (authStore.authenticated ? 2 : 1));
|
||||
// aal1
|
||||
const username = ref("");
|
||||
const password = ref("");
|
||||
|
@ -25,7 +25,7 @@ async function addAuthenticator() {
|
||||
const publicKeyCredential = credential as PublicKeyCredential;
|
||||
console.log("credential", credential);
|
||||
const name = "DummyAuth";
|
||||
await useInternalApi("webauthn/register", "POST", {
|
||||
await useInternalApi("auth/webauthn/register", "POST", {
|
||||
name,
|
||||
credentialId: credential.id,
|
||||
clientData: encodeBase64Url(publicKeyCredential.response.clientDataJSON),
|
||||
|
Loading…
Reference in New Issue
Block a user