This commit is contained in:
Jessie 2020-08-22 17:43:39 -04:00 committed by GitHub
commit 6b065d1f3e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 73 additions and 8 deletions

View file

@ -22,6 +22,7 @@
<link rel='apple-touch-startup-image' href='img/apple-touch-startup-image-1668x2224.png' media='(min-device-width: 834px) and (max-device-width: 834px) and (-webkit-min-device-pixel-ratio: 2) and (orientation: portrait)'/>
<link rel='apple-touch-startup-image' href='img/apple-touch-startup-image-2048x2732.png' media='(min-device-width: 1024px) and (max-device-width: 1024px) and (-webkit-min-device-pixel-ratio: 2) and (orientation: portrait)'/>
<link rel='manifest' href='manifest.json'/>
<script src="https://www.google.com/recaptcha/api.js"></script>
</head>
<body>
<div id='top-navigation-holder'></div>

View file

@ -38,6 +38,7 @@
<div class='messages'></div>
<div class='buttons'>
<% if(ctx.enableRecaptcha) print(`<div id="recaptcha"></div><br>`); %>
<input type='submit' value='Create an account'/>
</div>
</form>

View file

@ -100,6 +100,10 @@ class Api extends events.EventTarget {
return remoteConfig.contactEmail;
}
getRecaptchaSiteKey() {
return remoteConfig.recaptchaSiteKey;
}
canSendMails() {
return !!remoteConfig.canSendMails;
}
@ -108,6 +112,10 @@ class Api extends events.EventTarget {
return !!remoteConfig.enableSafety;
}
recaptchaEnabled() {
return !this.isLoggedIn() && !!remoteConfig.enableRecaptcha;
}
hasPrivilege(lookup) {
let minViableRank = null;
for (let p of Object.keys(remoteConfig.privileges)) {

View file

@ -30,7 +30,7 @@ class UserRegistrationController {
user.email = e.detail.email;
user.password = e.detail.password;
const isLoggedIn = api.isLoggedIn();
user.save()
user.save(e.detail.recaptchaToken)
.then(() => {
if (isLoggedIn) {
return Promise.resolve();

View file

@ -107,7 +107,7 @@ class User extends events.EventTarget {
});
}
save() {
save(recaptchaToken) {
const files = [];
const detail = { version: this._version };
const transient = this._orig._name;
@ -131,13 +131,16 @@ class User extends events.EventTarget {
if (this._password) {
detail.password = this._password;
}
if (api.recaptchaEnabled) {
detail.recaptchaToken = recaptchaToken;
}
let promise = this._orig._name
? api.put(
uri.formatApiLink("user", this._orig._name),
detail,
files
)
uri.formatApiLink("user", this._orig._name),
detail,
files
)
: api.post(uri.formatApiLink("users"), detail, files);
return promise.then((response) => {

View file

@ -15,11 +15,27 @@ class RegistrationView extends events.EventTarget {
template({
userNamePattern: api.getUserNameRegex(),
passwordPattern: api.getPasswordRegex(),
enableRecaptcha: api.recaptchaEnabled(),
})
);
views.syncScrollPosition();
views.decorateValidator(this._formNode);
this._formNode.addEventListener("submit", (e) => this._evtSubmit(e));
this.setRecaptchaToken = this.setRecaptchaToken.bind(this);
if (api.recaptchaEnabled())
this.renderRecaptcha();
}
renderRecaptcha() {
grecaptcha.render(this._recaptchaNode, {
"callback": this.setRecaptchaToken,
"sitekey": api.getRecaptchaSiteKey(),
});
}
setRecaptchaToken(token) {
this.recaptchaToken = token;
}
clearMessages() {
@ -46,6 +62,7 @@ class RegistrationView extends events.EventTarget {
name: this._userNameFieldNode.value,
password: this._passwordFieldNode.value,
email: this._emailFieldNode.value,
recaptchaToken: this.recaptchaToken,
},
})
);
@ -66,6 +83,10 @@ class RegistrationView extends events.EventTarget {
get _emailFieldNode() {
return this._formNode.querySelector("[name=email]");
}
get _recaptchaNode() {
return this._formNode.querySelector("#recaptcha");
}
}
module.exports = RegistrationView;

View file

@ -24,7 +24,7 @@ RUN \
alembic \
"coloredlogs==5.0" \
youtube-dl \
&& apk --no-cache del py3-pip
requests
ARG PUID=1000
ARG PGID=1000

View file

@ -8,6 +8,15 @@ domain: # example: http://example.com
# used to salt the users' password hashes and generate filenames for static content
secret: change
# Whether solving a captcha is required for registration for anonymous users.
enable_recaptcha: no
# The reCAPTCHA site key.
recaptcha_site_key: change
# A reCAPTCHA v2 secret token.
# https://developers.google.com/recaptcha/intro
# https://developers.google.com/recaptcha/docs/display
recaptcha_secret: change
# Delete thumbnails and source files on post delete
# Original functionality is no, to mitigate the impacts of admins going
# on unchecked post purges.

View file

@ -10,3 +10,4 @@ pynacl>=1.2.1
pytz>=2018.3
pyRFC3339>=1.0
youtube-dl
requests

View file

@ -49,6 +49,8 @@ def get_info(ctx: rest.Context, _params: Dict[str, str] = {}) -> rest.Response:
"privileges": util.snake_case_to_lower_camel_case_keys(
config.config["privileges"]
),
"enableRecaptcha": config.config["enable_recaptcha"],
"recaptchaSiteKey": config.config["recaptcha_site_key"],
},
}
if auth.has_privilege(ctx.user, "posts:view:featured"):

View file

@ -1,6 +1,8 @@
from typing import Any, Dict
from szurubooru import model, rest, search
import requests
from szurubooru import config, model, rest, search
from szurubooru.func import auth, serialization, users, versions
_search_executor = search.Executor(search.configs.UserSearchConfig())
@ -31,11 +33,28 @@ def get_users(
def create_user(
ctx: rest.Context, _params: Dict[str, str] = {}
) -> rest.Response:
expect_recaptcha = False
if ctx.user.user_id is None:
expect_recaptcha = True
auth.verify_privilege(ctx.user, "users:create:self")
else:
auth.verify_privilege(ctx.user, "users:create:any")
# Verify if the recaptcha was correct.
if expect_recaptcha and config.config["enable_recaptcha"]:
resp = requests.post("https://www.google.com/recaptcha/api/siteverify", data={
"secret": config.config["recaptcha_secret"],
"response": ctx.get_param_as_string("recaptchaToken", default=""),
})
# Raise a 400 error if the recaptcha wasn't OK.
if not resp.json()["success"]:
raise rest.errors.HttpBadRequest(
"ValidationError",
"Recaptcha response was invalid."
)
name = ctx.get_param_as_string("name")
password = ctx.get_param_as_string("password")
email = ctx.get_param_as_string("email", default="")