API & Client - update registration activation on user register/delete
+ minor refactor on administration (fix #15)
This commit is contained in:
parent
e28d609013
commit
5d7b63f7aa
@ -1,4 +1,6 @@
|
|||||||
from fittrackee_api import db
|
from fittrackee_api import db
|
||||||
|
from flask import current_app
|
||||||
|
from sqlalchemy.event import listens_for
|
||||||
|
|
||||||
from ..users.models import User
|
from ..users.models import User
|
||||||
|
|
||||||
@ -26,3 +28,25 @@ class AppConfig(db.Model):
|
|||||||
"max_zip_file_size": self.max_zip_file_size,
|
"max_zip_file_size": self.max_zip_file_size,
|
||||||
"max_users": self.max_users,
|
"max_users": self.max_users,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def update_app_config():
|
||||||
|
config = AppConfig.query.first()
|
||||||
|
if config:
|
||||||
|
current_app.config[
|
||||||
|
'is_registration_enabled'
|
||||||
|
] = config.is_registration_enabled
|
||||||
|
|
||||||
|
|
||||||
|
@listens_for(User, 'after_insert')
|
||||||
|
def on_user_insert(mapper, connection, user):
|
||||||
|
@listens_for(db.Session, 'after_flush', once=True)
|
||||||
|
def receive_after_flush(session, context):
|
||||||
|
update_app_config()
|
||||||
|
|
||||||
|
|
||||||
|
@listens_for(User, 'after_delete')
|
||||||
|
def on_user_delete(mapper, connection, old_user):
|
||||||
|
@listens_for(db.Session, 'after_flush', once=True)
|
||||||
|
def receive_after_flush(session, context):
|
||||||
|
update_app_config()
|
||||||
|
@ -62,7 +62,6 @@ def app_config():
|
|||||||
config.max_single_file_size = 1048576
|
config.max_single_file_size = 1048576
|
||||||
config.max_zip_file_size = 10485760
|
config.max_zip_file_size = 10485760
|
||||||
config.max_users = 0
|
config.max_users = 0
|
||||||
config.registration = False
|
|
||||||
db.session.add(config)
|
db.session.add(config)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
return config
|
return config
|
||||||
|
@ -836,3 +836,70 @@ def test_update_user_invalid_picture(app, user_1):
|
|||||||
assert data['status'] == 'fail'
|
assert data['status'] == 'fail'
|
||||||
assert data['message'] == 'File extension not allowed.'
|
assert data['message'] == 'File extension not allowed.'
|
||||||
assert response.status_code == 400
|
assert response.status_code == 400
|
||||||
|
|
||||||
|
|
||||||
|
def test_it_disables_registration_on_user_registration(
|
||||||
|
app_no_config, app_config, user_1_admin, user_2
|
||||||
|
):
|
||||||
|
app_config.max_users = 3
|
||||||
|
client = app_no_config.test_client()
|
||||||
|
client.post(
|
||||||
|
'/api/auth/register',
|
||||||
|
data=json.dumps(
|
||||||
|
dict(
|
||||||
|
username='sam',
|
||||||
|
email='sam@test.com',
|
||||||
|
password='12345678',
|
||||||
|
password_conf='12345678',
|
||||||
|
)
|
||||||
|
),
|
||||||
|
content_type='application/json',
|
||||||
|
)
|
||||||
|
response = client.post(
|
||||||
|
'/api/auth/register',
|
||||||
|
data=json.dumps(
|
||||||
|
dict(
|
||||||
|
username='new',
|
||||||
|
email='new@test.com',
|
||||||
|
password='12345678',
|
||||||
|
password_conf='12345678',
|
||||||
|
)
|
||||||
|
),
|
||||||
|
content_type='application/json',
|
||||||
|
)
|
||||||
|
assert response.status_code == 403
|
||||||
|
data = json.loads(response.data.decode())
|
||||||
|
assert data['status'] == 'error'
|
||||||
|
assert data['message'] == 'Error. Registration is disabled.'
|
||||||
|
|
||||||
|
|
||||||
|
def test_it_does_not_disable_registration_on_user_registration(
|
||||||
|
app_no_config, app_config, user_1_admin, user_2,
|
||||||
|
):
|
||||||
|
app_config.max_users = 4
|
||||||
|
client = app_no_config.test_client()
|
||||||
|
client.post(
|
||||||
|
'/api/auth/register',
|
||||||
|
data=json.dumps(
|
||||||
|
dict(
|
||||||
|
username='sam',
|
||||||
|
email='sam@test.com',
|
||||||
|
password='12345678',
|
||||||
|
password_conf='12345678',
|
||||||
|
)
|
||||||
|
),
|
||||||
|
content_type='application/json',
|
||||||
|
)
|
||||||
|
response = client.post(
|
||||||
|
'/api/auth/register',
|
||||||
|
data=json.dumps(
|
||||||
|
dict(
|
||||||
|
username='new',
|
||||||
|
email='new@test.com',
|
||||||
|
password='12345678',
|
||||||
|
password_conf='12345678',
|
||||||
|
)
|
||||||
|
),
|
||||||
|
content_type='application/json',
|
||||||
|
)
|
||||||
|
assert response.status_code == 201
|
||||||
|
@ -1252,3 +1252,70 @@ def test_admin_can_not_delete_its_own_account_if_no_other_admin(
|
|||||||
'You can not delete your account, no other user has admin rights.'
|
'You can not delete your account, no other user has admin rights.'
|
||||||
in data['message']
|
in data['message']
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_it_enables_registration_on_user_delete(
|
||||||
|
app_no_config, app_config, user_1_admin, user_2, user_3
|
||||||
|
):
|
||||||
|
app_config.max_users = 3
|
||||||
|
client = app_no_config.test_client()
|
||||||
|
resp_login = client.post(
|
||||||
|
'/api/auth/login',
|
||||||
|
data=json.dumps(dict(email='admin@example.com', password='12345678')),
|
||||||
|
content_type='application/json',
|
||||||
|
)
|
||||||
|
client.delete(
|
||||||
|
'/api/users/toto',
|
||||||
|
headers=dict(
|
||||||
|
Authorization='Bearer '
|
||||||
|
+ json.loads(resp_login.data.decode())['auth_token']
|
||||||
|
),
|
||||||
|
)
|
||||||
|
response = client.post(
|
||||||
|
'/api/auth/register',
|
||||||
|
data=json.dumps(
|
||||||
|
dict(
|
||||||
|
username='justatest',
|
||||||
|
email='test@test.com',
|
||||||
|
password='12345678',
|
||||||
|
password_conf='12345678',
|
||||||
|
)
|
||||||
|
),
|
||||||
|
content_type='application/json',
|
||||||
|
)
|
||||||
|
assert response.status_code == 201
|
||||||
|
|
||||||
|
|
||||||
|
def test_it_does_not_enable_registration_on_user_delete(
|
||||||
|
app_no_config, app_config, user_1_admin, user_2, user_3
|
||||||
|
):
|
||||||
|
app_config.max_users = 2
|
||||||
|
client = app_no_config.test_client()
|
||||||
|
resp_login = client.post(
|
||||||
|
'/api/auth/login',
|
||||||
|
data=json.dumps(dict(email='admin@example.com', password='12345678')),
|
||||||
|
content_type='application/json',
|
||||||
|
)
|
||||||
|
client.delete(
|
||||||
|
'/api/users/toto',
|
||||||
|
headers=dict(
|
||||||
|
Authorization='Bearer '
|
||||||
|
+ json.loads(resp_login.data.decode())['auth_token']
|
||||||
|
),
|
||||||
|
)
|
||||||
|
response = client.post(
|
||||||
|
'/api/auth/register',
|
||||||
|
data=json.dumps(
|
||||||
|
dict(
|
||||||
|
username='justatest',
|
||||||
|
email='test@test.com',
|
||||||
|
password='12345678',
|
||||||
|
password_conf='12345678',
|
||||||
|
)
|
||||||
|
),
|
||||||
|
content_type='application/json',
|
||||||
|
)
|
||||||
|
assert response.status_code == 403
|
||||||
|
data = json.loads(response.data.decode())
|
||||||
|
assert data['status'] == 'error'
|
||||||
|
assert data['message'] == 'Error. Registration is disabled.'
|
||||||
|
@ -3,6 +3,7 @@ import FitTrackeeApi from '../fitTrackeeApi/auth'
|
|||||||
import { history } from '../index'
|
import { history } from '../index'
|
||||||
import { generateIds } from '../utils'
|
import { generateIds } from '../utils'
|
||||||
import { getOrUpdateData, setError, updateLanguage } from './index'
|
import { getOrUpdateData, setError, updateLanguage } from './index'
|
||||||
|
import { getAppData } from './application'
|
||||||
|
|
||||||
const AuthError = message => ({ type: 'AUTH_ERROR', message })
|
const AuthError = message => ({ type: 'AUTH_ERROR', message })
|
||||||
|
|
||||||
@ -50,6 +51,9 @@ export const loginOrRegister = (target, formData) => dispatch =>
|
|||||||
.then(ret => {
|
.then(ret => {
|
||||||
if (ret.status === 'success') {
|
if (ret.status === 'success') {
|
||||||
window.localStorage.setItem('authToken', ret.auth_token)
|
window.localStorage.setItem('authToken', ret.auth_token)
|
||||||
|
if (target === 'register') {
|
||||||
|
dispatch(getAppData('config'))
|
||||||
|
}
|
||||||
return dispatch(getProfile())
|
return dispatch(getProfile())
|
||||||
}
|
}
|
||||||
return dispatch(AuthError(ret.message))
|
return dispatch(AuthError(ret.message))
|
||||||
@ -138,6 +142,7 @@ export const deleteUser = (username, isAdmin = false) => dispatch =>
|
|||||||
FitTrackeeGenericApi.deleteData('users', username)
|
FitTrackeeGenericApi.deleteData('users', username)
|
||||||
.then(ret => {
|
.then(ret => {
|
||||||
if (ret.status === 204) {
|
if (ret.status === 204) {
|
||||||
|
dispatch(getAppData('config'))
|
||||||
if (isAdmin) {
|
if (isAdmin) {
|
||||||
history.push('/admin/users')
|
history.push('/admin/users')
|
||||||
} else {
|
} else {
|
||||||
|
@ -82,8 +82,15 @@ class AdminApplication extends React.Component {
|
|||||||
>
|
>
|
||||||
{t(
|
{t(
|
||||||
// eslint-disable-next-line max-len
|
// eslint-disable-next-line max-len
|
||||||
'administration:Max. number of active users (if 0, no limitation)'
|
'administration:Max. number of active users'
|
||||||
)}
|
)}
|
||||||
|
<sup>
|
||||||
|
<i
|
||||||
|
className="fa fa-question-circle"
|
||||||
|
aria-hidden="true"
|
||||||
|
title={t('administration:if 0, no limitation')}
|
||||||
|
/>
|
||||||
|
</sup>
|
||||||
:
|
:
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@ -197,7 +204,6 @@ class AdminApplication extends React.Component {
|
|||||||
|
|
||||||
export default connect(
|
export default connect(
|
||||||
state => ({
|
state => ({
|
||||||
appConfig: state.application.config,
|
|
||||||
message: state.message,
|
message: state.message,
|
||||||
}),
|
}),
|
||||||
dispatch => ({
|
dispatch => ({
|
||||||
|
@ -4,7 +4,7 @@ import { Link } from 'react-router-dom'
|
|||||||
import AdminStats from './AdminStats'
|
import AdminStats from './AdminStats'
|
||||||
|
|
||||||
export default function AdminDashboard(props) {
|
export default function AdminDashboard(props) {
|
||||||
const { t } = props
|
const { appConfig, t } = props
|
||||||
return (
|
return (
|
||||||
<div className="card activity-card">
|
<div className="card activity-card">
|
||||||
<div className="card-header">
|
<div className="card-header">
|
||||||
@ -27,7 +27,15 @@ export default function AdminDashboard(props) {
|
|||||||
{t(
|
{t(
|
||||||
'administration:Update application configuration ' +
|
'administration:Update application configuration ' +
|
||||||
'(maximum number of registered users, maximum files size).'
|
'(maximum number of registered users, maximum files size).'
|
||||||
)}{' '}
|
)}
|
||||||
|
<br />
|
||||||
|
<strong>
|
||||||
|
{t(
|
||||||
|
`administration:Registration is currently ${
|
||||||
|
appConfig.is_registration_enabled ? 'enabled' : 'disabled'
|
||||||
|
}.`
|
||||||
|
)}
|
||||||
|
</strong>
|
||||||
</dd>
|
</dd>
|
||||||
<br />
|
<br />
|
||||||
<dt>
|
<dt>
|
||||||
|
@ -11,7 +11,7 @@ import AdminUsers from './AdminUsers'
|
|||||||
import NotFound from './../Others/NotFound'
|
import NotFound from './../Others/NotFound'
|
||||||
|
|
||||||
function Admin(props) {
|
function Admin(props) {
|
||||||
const { t, user } = props
|
const { appConfig, t, user } = props
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Helmet>
|
<Helmet>
|
||||||
@ -23,12 +23,12 @@ function Admin(props) {
|
|||||||
<Route
|
<Route
|
||||||
exact
|
exact
|
||||||
path="/admin"
|
path="/admin"
|
||||||
render={() => <AdminDashboard t={t} />}
|
render={() => <AdminDashboard appConfig={appConfig} t={t} />}
|
||||||
/>
|
/>
|
||||||
<Route
|
<Route
|
||||||
exact
|
exact
|
||||||
path="/admin/application"
|
path="/admin/application"
|
||||||
render={() => <AdminApplication t={t} />}
|
render={() => <AdminApplication appConfig={appConfig} t={t} />}
|
||||||
/>
|
/>
|
||||||
<Route
|
<Route
|
||||||
exact
|
exact
|
||||||
@ -52,6 +52,7 @@ function Admin(props) {
|
|||||||
|
|
||||||
export default withTranslation()(
|
export default withTranslation()(
|
||||||
connect(state => ({
|
connect(state => ({
|
||||||
|
appConfig: state.application.config,
|
||||||
user: state.user,
|
user: state.user,
|
||||||
}))(Admin)
|
}))(Admin)
|
||||||
)
|
)
|
||||||
|
@ -13,14 +13,17 @@
|
|||||||
"Enable/disable sports.": "Enable/disable sports.",
|
"Enable/disable sports.": "Enable/disable sports.",
|
||||||
"FitTrackee administration": "FitTrackee administration",
|
"FitTrackee administration": "FitTrackee administration",
|
||||||
"id": "id",
|
"id": "id",
|
||||||
|
"if 0, no limitation": "if 0, no limitation",
|
||||||
"Image": "Image",
|
"Image": "Image",
|
||||||
"Label": "Label",
|
"Label": "Label",
|
||||||
"Max. number of active users (if 0, no limitation)": "Max. number of active users",
|
"Max. number of active users": "Max. number of active users",
|
||||||
"Max. files of zip archive": "Max. files of zip archive",
|
"Max. files of zip archive": "Max. files of zip archive",
|
||||||
"Max. size of uploaded files": "Max. size of uploaded files",
|
"Max. size of uploaded files": "Max. size of uploaded files",
|
||||||
"Max. size of uploaded files (in Mb)": "Max. size of uploaded files (in Mb)",
|
"Max. size of uploaded files (in Mb)": "Max. size of uploaded files (in Mb)",
|
||||||
"Max. size of zip archive": "Max. size of zip archive",
|
"Max. size of zip archive": "Max. size of zip archive",
|
||||||
"Max. size of zip archive (in Mb)": "Max. size of zip archive (in Mb)",
|
"Max. size of zip archive (in Mb)": "Max. size of zip archive (in Mb)",
|
||||||
|
"Registration is currently disabled.": "Registration is currently disabled.",
|
||||||
|
"Registration is currently enabled.": "Registration is currently enabled.",
|
||||||
"Remove admin rights": "Remove admin rights",
|
"Remove admin rights": "Remove admin rights",
|
||||||
"Sports": "Sports",
|
"Sports": "Sports",
|
||||||
"Update application configuration (maximum number of registered users, maximum files size).": "Update application configuration (maximum number of registered users, maximum files size).",
|
"Update application configuration (maximum number of registered users, maximum files size).": "Update application configuration (maximum number of registered users, maximum files size).",
|
||||||
|
@ -13,14 +13,17 @@
|
|||||||
"Enable/disable sports.": "Activer/désactiver des sports.",
|
"Enable/disable sports.": "Activer/désactiver des sports.",
|
||||||
"FitTrackee administration": "Administration de FitTrackee",
|
"FitTrackee administration": "Administration de FitTrackee",
|
||||||
"id": "id",
|
"id": "id",
|
||||||
|
"if 0, no limitation": "si égal à 0, pas limite d'inscription",
|
||||||
"Image": "Image",
|
"Image": "Image",
|
||||||
"Label": "Label",
|
"Label": "Label",
|
||||||
"Max. number of active users (if 0, no limitation)": "Nombre maximum d'utilisateurs actifs",
|
"Max. number of active users": "Nombre maximum d'utilisateurs actifs",
|
||||||
"Max. files of zip archive": "Nombre max. de fichiers dans une archive zip",
|
"Max. files of zip archive": "Nombre max. de fichiers dans une archive zip",
|
||||||
"Max. size of uploaded files": "Taille max. des fichiers",
|
"Max. size of uploaded files": "Taille max. des fichiers",
|
||||||
"Max. size of uploaded files (in Mb)": "Taille max. des fichiers (en Mo)",
|
"Max. size of uploaded files (in Mb)": "Taille max. des fichiers (en Mo)",
|
||||||
"Max. size of zip archive": "Taille max. des archives zip",
|
"Max. size of zip archive": "Taille max. des archives zip",
|
||||||
"Max. size of zip archive (in Mb)": "Taille max. des archives zip (en Mo)",
|
"Max. size of zip archive (in Mb)": "Taille max. des archives zip (en Mo)",
|
||||||
|
"Registration is currently disabled.": "Les inscriptions sont actuellement désactivées",
|
||||||
|
"Registration is currently enabled.": "Les inscriptions sont actuellement activées",
|
||||||
"Remove admin rights": "Retirer des droits d'admin",
|
"Remove admin rights": "Retirer des droits d'admin",
|
||||||
"Sports": "Sports",
|
"Sports": "Sports",
|
||||||
"Update application configuration (maximum number of registered users, maximum files size).": "Configurer l'application (nombre maximum d'utilisateurs inscrits, taille maximale des fichers).",
|
"Update application configuration (maximum number of registered users, maximum files size).": "Configurer l'application (nombre maximum d'utilisateurs inscrits, taille maximale des fichers).",
|
||||||
|
Loading…
Reference in New Issue
Block a user