mirror of
https://github.com/HangarMC/Hangar.git
synced 2024-11-27 06:01:08 +08:00
feat: add turnstile as captcha provider to signup
This commit is contained in:
parent
b534447f30
commit
bbe3335935
@ -13,6 +13,7 @@ import io.papermc.hangar.components.auth.model.dto.SignupForm;
|
||||
import io.papermc.hangar.components.auth.service.AuthService;
|
||||
import io.papermc.hangar.components.auth.service.CredentialsService;
|
||||
import io.papermc.hangar.components.auth.service.TokenService;
|
||||
import io.papermc.hangar.components.auth.service.TurnstileService;
|
||||
import io.papermc.hangar.components.auth.service.VerificationService;
|
||||
import io.papermc.hangar.exceptions.HangarApiException;
|
||||
import io.papermc.hangar.model.db.UserTable;
|
||||
@ -47,18 +48,22 @@ public class AuthController extends HangarComponent {
|
||||
private final VerificationService verificationService;
|
||||
private final CredentialsService credentialsService;
|
||||
private final UserService userService;
|
||||
private final TurnstileService turnstileService;
|
||||
|
||||
public AuthController(final AuthService authService, final TokenService tokenService, final VerificationService verificationService, final CredentialsService credentialsService, final UserService userService) {
|
||||
public AuthController(final AuthService authService, final TokenService tokenService, final VerificationService verificationService, final CredentialsService credentialsService, final UserService userService, final TurnstileService turnstileService) {
|
||||
this.authService = authService;
|
||||
this.tokenService = tokenService;
|
||||
this.verificationService = verificationService;
|
||||
this.credentialsService = credentialsService;
|
||||
this.userService = userService;
|
||||
this.turnstileService = turnstileService;
|
||||
}
|
||||
|
||||
@Anyone
|
||||
@PostMapping("/signup")
|
||||
public ResponseEntity<?> signup(@RequestBody final SignupForm signupForm) {
|
||||
this.turnstileService.validate(signupForm.captcha());
|
||||
|
||||
final UserTable userTable = this.authService.registerUser(signupForm);
|
||||
if (userTable == null) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
|
@ -1,4 +1,4 @@
|
||||
package io.papermc.hangar.components.auth.model.dto;
|
||||
|
||||
public record SignupForm(String username, String email, String password, boolean tos) {
|
||||
public record SignupForm(String username, String email, String password, boolean tos, String captcha) {
|
||||
}
|
||||
|
@ -0,0 +1,43 @@
|
||||
package io.papermc.hangar.components.auth.service;
|
||||
|
||||
import io.papermc.hangar.HangarComponent;
|
||||
import io.papermc.hangar.exceptions.HangarApiException;
|
||||
import io.papermc.hangar.util.RequestUtil;
|
||||
import java.util.Arrays;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
@Service
|
||||
public class TurnstileService extends HangarComponent {
|
||||
|
||||
private final RestTemplate restTemplate;
|
||||
|
||||
public TurnstileService(final RestTemplate restTemplate) {
|
||||
this.restTemplate = restTemplate;
|
||||
}
|
||||
|
||||
public void validate(String token) {
|
||||
if (this.config.security.turnstileSecret() != null && !this.config.security.turnstileSecret().isBlank()) {
|
||||
MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
|
||||
formData.add("secret", this.config.security.turnstileSecret());
|
||||
formData.add("response", token);
|
||||
formData.add("remoteip", RequestUtil.getRemoteAddress(this.request));
|
||||
|
||||
MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
|
||||
headers.set("User-Agent", "Hangar/1.0");
|
||||
HttpEntity<MultiValueMap<String, String>> entity = new HttpEntity<>(formData, headers);
|
||||
|
||||
String url = "https://challenges.cloudflare.com/turnstile/v0/siteverify";
|
||||
var response = this.restTemplate.postForEntity(url, entity, TurnstileResponse.class);
|
||||
|
||||
if (response.getBody() != null && !response.getBody().success()) {
|
||||
throw new HangarApiException("error.captcha", Arrays.toString(response.getBody().errorCodes()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
record TurnstileResponse(boolean success, String[] errorCodes){}
|
||||
}
|
@ -21,7 +21,8 @@ public record HangarSecurityConfig(
|
||||
String rpName,
|
||||
String rpId,
|
||||
List<OAuthProvider> oAuthProviders,
|
||||
@DefaultValue("false") boolean oAuthEnabled
|
||||
@DefaultValue("false") boolean oAuthEnabled,
|
||||
String turnstileSecret
|
||||
) {
|
||||
|
||||
public boolean checkSafe(final String url) {
|
||||
|
@ -166,6 +166,7 @@ hangar:
|
||||
refresh-token-expiry: 30 # days
|
||||
rp-name: "Hangar"
|
||||
rp-id: "localhost"
|
||||
turnstileSecret: ""
|
||||
safe-download-hosts:
|
||||
- "dev.bukkit.org"
|
||||
- "github.com"
|
||||
|
@ -95,9 +95,9 @@ public class TestData {
|
||||
HangarApplication.TEST_MODE = true;
|
||||
logger.info("Preparing test data...");
|
||||
logger.info("Creating some test users...");
|
||||
USER_NORMAL = this.authService.registerUser(new SignupForm("TestUser", "testuser@papermc.io", "W45nNUefrsB8ucQeiKDdbEQijH5KP", true));
|
||||
USER_MEMBER = this.authService.registerUser(new SignupForm("TestMember", "testmember@papermc.io", "W45nNUefrsB8ucQeiKDdbEQijH5KP", true));
|
||||
USER_ADMIN = this.authService.registerUser(new SignupForm("TestAdmin", "testadmin@papermc.io", "W45nNUefrsB8ucQeiKDdbEQijH5KP", true));
|
||||
USER_NORMAL = this.authService.registerUser(new SignupForm("TestUser", "testuser@papermc.io", "W45nNUefrsB8ucQeiKDdbEQijH5KP", true, null));
|
||||
USER_MEMBER = this.authService.registerUser(new SignupForm("TestMember", "testmember@papermc.io", "W45nNUefrsB8ucQeiKDdbEQijH5KP", true, null));
|
||||
USER_ADMIN = this.authService.registerUser(new SignupForm("TestAdmin", "testadmin@papermc.io", "W45nNUefrsB8ucQeiKDdbEQijH5KP", true, null));
|
||||
|
||||
USER_NORMAL.setEmailVerified(true);
|
||||
USER_MEMBER.setEmailVerified(true);
|
||||
|
@ -50,6 +50,7 @@ stringData:
|
||||
security:
|
||||
token-secret: "{{ .Values.backend.config.tokenSecret }}"
|
||||
rp-id: "{{ .Values.backend.config.rpId }}"
|
||||
turnstile-secret: "{{ .Values.backend.config.turnstileSecret }}"
|
||||
o-auth-enabled: {{ .Values.backend.config.oauthEnabled }}
|
||||
o-auth-providers:
|
||||
- name: "github"
|
||||
|
@ -13,3 +13,4 @@ stringData:
|
||||
#DEBUG: "hangar:*"
|
||||
#NITRO_CLUSTER_WORKERS: "4"
|
||||
SENTRY_ENV: "{{ .Values.backend.config.sentry.environment }}"
|
||||
NUXT_PUBLIC_TURNSTILE_SITE_KEY: "{{ .Values.frontend.config.turnstileSiteKey }}"
|
||||
|
@ -111,6 +111,7 @@ frontend:
|
||||
config:
|
||||
configEnv: "hangar.test"
|
||||
backendHost: "http://hangar-backend:8080"
|
||||
turnstileSiteKey: "todo"
|
||||
|
||||
backend:
|
||||
replicaCount: 1
|
||||
@ -186,6 +187,7 @@ backend:
|
||||
options: "?currentSchema=hangar"
|
||||
tokenSecret: "secret"
|
||||
rpId: "localhost"
|
||||
turnstileSecret: ""
|
||||
oauthEnabled: true
|
||||
githubClientId: "todo"
|
||||
githubClientSecret: "todo"
|
||||
|
@ -56,6 +56,7 @@ export default defineNuxtConfig({
|
||||
"@vueuse/nuxt",
|
||||
"@nuxtjs/i18n",
|
||||
"@sentry/nuxt/module",
|
||||
"@nuxtjs/turnstile",
|
||||
[
|
||||
"unplugin-icons/nuxt",
|
||||
{
|
||||
|
@ -65,6 +65,7 @@
|
||||
"@iconify-json/mdi": "1.2.1",
|
||||
"@nuxtjs/eslint-config-typescript": "12.1.0",
|
||||
"@nuxtjs/i18n": "8.5.5",
|
||||
"@nuxtjs/turnstile": "0.9.11",
|
||||
"@sentry/bun": "8.37.1",
|
||||
"@sentry/nuxt": "8.37.1",
|
||||
"@sentry/profiling-node": "8.37.1",
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -8,8 +8,11 @@ interface SignupForm {
|
||||
email?: string;
|
||||
password?: string;
|
||||
tos?: boolean;
|
||||
captcha?: string;
|
||||
}
|
||||
|
||||
const config = useRuntimeConfig();
|
||||
|
||||
const done = ref(false);
|
||||
|
||||
const notification = useNotificationStore();
|
||||
@ -90,6 +93,7 @@ useSeo(computed(() => ({ title: "Sign up", route })));
|
||||
<InputText v-model="form.username" label="Username" name="username" autocomplete="username" :rules="[required()]" />
|
||||
<InputText v-model="form.email" type="email" label="E-Mail" name="email" autocomplete="email" :rules="[required(), email()]" />
|
||||
<InputPassword v-model="form.password" label="Password" name="new-password" :rules="[required()]" />
|
||||
<LazyNuxtTurnstile v-if="config.public.turnstile?.siteKey != '1x00000000000000000000AA'" v-model="form.captcha" />
|
||||
<div v-if="errorMessage" class="c-red">{{ errorMessage }}</div>
|
||||
<Button type="submit" :disabled="loading" @click.prevent="submit">Sign up</Button>
|
||||
<div class="w-max">
|
||||
|
@ -100,6 +100,11 @@ site. We strongly advise You to review the Privacy Policy of every site You visi
|
||||
|
||||
We have no control over and assume no responsibility for the content, privacy policies or practices of any third party sites or services.
|
||||
|
||||
Cloudflare
|
||||
----------
|
||||
|
||||
This Website is protected by various Cloudflare technologies. You can view their Privacy Policy here: https://www.cloudflare.com/privacypolicy/
|
||||
|
||||
Changes to this Privacy Policy
|
||||
------------------------------
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user