Merge 33c7752cfa
into 74c97efdef
This commit is contained in:
commit
6b065d1f3e
11 changed files with 73 additions and 8 deletions
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -24,7 +24,7 @@ RUN \
|
|||
alembic \
|
||||
"coloredlogs==5.0" \
|
||||
youtube-dl \
|
||||
&& apk --no-cache del py3-pip
|
||||
requests
|
||||
|
||||
ARG PUID=1000
|
||||
ARG PGID=1000
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -10,3 +10,4 @@ pynacl>=1.2.1
|
|||
pytz>=2018.3
|
||||
pyRFC3339>=1.0
|
||||
youtube-dl
|
||||
requests
|
|
@ -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"):
|
||||
|
|
|
@ -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="")
|
||||
|
|
Reference in a new issue