diff --git a/fittrackee_api/fittrackee_api/application/models.py b/fittrackee_api/fittrackee_api/application/models.py index e133001f..5bc63c99 100644 --- a/fittrackee_api/fittrackee_api/application/models.py +++ b/fittrackee_api/fittrackee_api/application/models.py @@ -1,4 +1,6 @@ from fittrackee_api import db +from flask import current_app +from sqlalchemy.event import listens_for from ..users.models import User @@ -26,3 +28,25 @@ class AppConfig(db.Model): "max_zip_file_size": self.max_zip_file_size, "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() diff --git a/fittrackee_api/fittrackee_api/tests/conftest.py b/fittrackee_api/fittrackee_api/tests/conftest.py index 97c35782..1fd865ba 100644 --- a/fittrackee_api/fittrackee_api/tests/conftest.py +++ b/fittrackee_api/fittrackee_api/tests/conftest.py @@ -62,7 +62,6 @@ def app_config(): config.max_single_file_size = 1048576 config.max_zip_file_size = 10485760 config.max_users = 0 - config.registration = False db.session.add(config) db.session.commit() return config diff --git a/fittrackee_api/fittrackee_api/tests/test_auth_api.py b/fittrackee_api/fittrackee_api/tests/test_auth_api.py index 8f02aea1..1ad4e8f9 100644 --- a/fittrackee_api/fittrackee_api/tests/test_auth_api.py +++ b/fittrackee_api/fittrackee_api/tests/test_auth_api.py @@ -836,3 +836,70 @@ def test_update_user_invalid_picture(app, user_1): assert data['status'] == 'fail' assert data['message'] == 'File extension not allowed.' 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 diff --git a/fittrackee_api/fittrackee_api/tests/test_users_api.py b/fittrackee_api/fittrackee_api/tests/test_users_api.py index 8976c399..a27898cb 100644 --- a/fittrackee_api/fittrackee_api/tests/test_users_api.py +++ b/fittrackee_api/fittrackee_api/tests/test_users_api.py @@ -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.' 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.' diff --git a/fittrackee_client/src/actions/user.js b/fittrackee_client/src/actions/user.js index a721892d..706a7857 100644 --- a/fittrackee_client/src/actions/user.js +++ b/fittrackee_client/src/actions/user.js @@ -3,6 +3,7 @@ import FitTrackeeApi from '../fitTrackeeApi/auth' import { history } from '../index' import { generateIds } from '../utils' import { getOrUpdateData, setError, updateLanguage } from './index' +import { getAppData } from './application' const AuthError = message => ({ type: 'AUTH_ERROR', message }) @@ -50,6 +51,9 @@ export const loginOrRegister = (target, formData) => dispatch => .then(ret => { if (ret.status === 'success') { window.localStorage.setItem('authToken', ret.auth_token) + if (target === 'register') { + dispatch(getAppData('config')) + } return dispatch(getProfile()) } return dispatch(AuthError(ret.message)) @@ -138,6 +142,7 @@ export const deleteUser = (username, isAdmin = false) => dispatch => FitTrackeeGenericApi.deleteData('users', username) .then(ret => { if (ret.status === 204) { + dispatch(getAppData('config')) if (isAdmin) { history.push('/admin/users') } else { diff --git a/fittrackee_client/src/components/Admin/AdminApplication.jsx b/fittrackee_client/src/components/Admin/AdminApplication.jsx index 9160047c..278d4959 100644 --- a/fittrackee_client/src/components/Admin/AdminApplication.jsx +++ b/fittrackee_client/src/components/Admin/AdminApplication.jsx @@ -82,8 +82,15 @@ class AdminApplication extends React.Component { > {t( // eslint-disable-next-line max-len - 'administration:Max. number of active users (if 0, no limitation)' + 'administration:Max. number of active users' )} + +