mirror of
https://github.com/aljazceru/CTFd.git
synced 2025-12-18 06:24:23 +01:00
* Fix removing profile details (Closes #894) * Update tests to properly check setting and removing profile values
This commit is contained in:
@@ -39,10 +39,13 @@ class UserSchema(ma.ModelSchema):
|
|||||||
website = field_for(
|
website = field_for(
|
||||||
Users,
|
Users,
|
||||||
'website',
|
'website',
|
||||||
validate=validate.URL(
|
validate=[
|
||||||
error='Websites must be a proper URL starting with http or https',
|
# This is a dirty hack to let website accept empty strings so you can remove your website
|
||||||
schemes={'http', 'https'}
|
lambda website: validate.URL(
|
||||||
)
|
error='Websites must be a proper URL starting with http or https',
|
||||||
|
schemes={'http', 'https'}
|
||||||
|
)(website) if website else True
|
||||||
|
]
|
||||||
)
|
)
|
||||||
country = field_for(
|
country = field_for(
|
||||||
Users,
|
Users,
|
||||||
@@ -54,9 +57,6 @@ class UserSchema(ma.ModelSchema):
|
|||||||
password = field_for(
|
password = field_for(
|
||||||
Users,
|
Users,
|
||||||
'password',
|
'password',
|
||||||
validate=[
|
|
||||||
validate.Length(min=1, error='Passwords must not be empty'),
|
|
||||||
]
|
|
||||||
)
|
)
|
||||||
|
|
||||||
@pre_load
|
@pre_load
|
||||||
@@ -123,12 +123,11 @@ class UserSchema(ma.ModelSchema):
|
|||||||
password = data.get('password')
|
password = data.get('password')
|
||||||
confirm = data.get('confirm')
|
confirm = data.get('confirm')
|
||||||
target_user = get_current_user()
|
target_user = get_current_user()
|
||||||
user_id = data.get('id')
|
|
||||||
|
|
||||||
if is_admin():
|
if is_admin():
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
if password and (confirm is None):
|
if password and (bool(confirm) is False):
|
||||||
raise ValidationError('Please confirm your current password', field_names=['confirm'])
|
raise ValidationError('Please confirm your current password', field_names=['confirm'])
|
||||||
|
|
||||||
if password and confirm:
|
if password and confirm:
|
||||||
@@ -137,6 +136,9 @@ class UserSchema(ma.ModelSchema):
|
|||||||
return data
|
return data
|
||||||
else:
|
else:
|
||||||
raise ValidationError('Your previous password is incorrect', field_names=['confirm'])
|
raise ValidationError('Your previous password is incorrect', field_names=['confirm'])
|
||||||
|
else:
|
||||||
|
data.pop('password', None)
|
||||||
|
data.pop('confirm', None)
|
||||||
|
|
||||||
views = {
|
views = {
|
||||||
'user': [
|
'user': [
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ $(function () {
|
|||||||
form.submit(function(e){
|
form.submit(function(e){
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
$('#results').empty();
|
$('#results').empty();
|
||||||
var params = $('#user-settings-form').serializeJSON(true);
|
var params = $('#user-settings-form').serializeJSON();
|
||||||
|
|
||||||
CTFd.fetch('/api/v1/users/me', {
|
CTFd.fetch('/api/v1/users/me', {
|
||||||
method: 'PATCH',
|
method: 'PATCH',
|
||||||
|
|||||||
@@ -72,7 +72,7 @@
|
|||||||
Country
|
Country
|
||||||
</label>
|
</label>
|
||||||
<select class="form-control" id="country-input" name="country">
|
<select class="form-control" id="country-input" name="country">
|
||||||
<option></option>
|
<option value=""></option>
|
||||||
{% set countries = get_countries() %}
|
{% set countries = get_countries() %}
|
||||||
{% for country_code in countries.keys() %}
|
{% for country_code in countries.keys() %}
|
||||||
<option value="{{ country_code }}" {% if country == country_code %}selected{% endif %}>{{ countries[country_code] }}</option>
|
<option value="{{ country_code }}" {% if country == country_code %}selected{% endif %}>{{ countries[country_code] }}</option>
|
||||||
|
|||||||
@@ -32,5 +32,7 @@ def unique_email(email, model=Users):
|
|||||||
|
|
||||||
|
|
||||||
def validate_country_code(country_code):
|
def validate_country_code(country_code):
|
||||||
|
if country_code.strip() == "":
|
||||||
|
return
|
||||||
if lookup_country_code(country_code) is None:
|
if lookup_country_code(country_code) is None:
|
||||||
raise ValidationError('Invalid Country')
|
raise ValidationError('Invalid Country')
|
||||||
|
|||||||
104
tests/users/test_settings.py
Normal file
104
tests/users/test_settings.py
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from CTFd.utils.crypto import verify_password
|
||||||
|
from tests.helpers import *
|
||||||
|
|
||||||
|
|
||||||
|
def test_user_set_profile():
|
||||||
|
"""Test that a user can set and remove their information in their profile"""
|
||||||
|
app = create_ctfd()
|
||||||
|
with app.app_context():
|
||||||
|
register_user(app)
|
||||||
|
client = login_as_user(app)
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'name': 'user',
|
||||||
|
'email': 'user@ctfd.io',
|
||||||
|
'confirm': '',
|
||||||
|
'password': '',
|
||||||
|
'affiliation': 'affiliation_test',
|
||||||
|
'website': 'https://ctfd.io',
|
||||||
|
'country': 'US',
|
||||||
|
}
|
||||||
|
|
||||||
|
r = client.patch('/api/v1/users/me', json=data)
|
||||||
|
assert r.status_code == 200
|
||||||
|
|
||||||
|
user = Users.query.filter_by(id=2).first()
|
||||||
|
assert user.affiliation == data['affiliation']
|
||||||
|
assert user.website == data['website']
|
||||||
|
assert user.country == data['country']
|
||||||
|
|
||||||
|
r = client.get('/settings')
|
||||||
|
resp = r.get_data(as_text=True)
|
||||||
|
for k, v in data.items():
|
||||||
|
assert v in resp
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'name': 'user',
|
||||||
|
'email': 'user@ctfd.io',
|
||||||
|
'confirm': '',
|
||||||
|
'password': '',
|
||||||
|
'affiliation': '',
|
||||||
|
'website': '',
|
||||||
|
'country': '',
|
||||||
|
}
|
||||||
|
|
||||||
|
r = client.patch('/api/v1/users/me', json=data)
|
||||||
|
assert r.status_code == 200
|
||||||
|
|
||||||
|
user = Users.query.filter_by(id=2).first()
|
||||||
|
assert user.affiliation == data['affiliation']
|
||||||
|
assert user.website == data['website']
|
||||||
|
assert user.country == data['country']
|
||||||
|
destroy_ctfd(app)
|
||||||
|
|
||||||
|
|
||||||
|
def test_user_can_change_password():
|
||||||
|
"""Test that a user can change their password and is prompted properly"""
|
||||||
|
app = create_ctfd()
|
||||||
|
with app.app_context():
|
||||||
|
register_user(app)
|
||||||
|
client = login_as_user(app)
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'name': 'user',
|
||||||
|
'email': 'user@ctfd.io',
|
||||||
|
'confirm': '',
|
||||||
|
'password': 'new_password',
|
||||||
|
'affiliation': '',
|
||||||
|
'website': '',
|
||||||
|
'country': '',
|
||||||
|
}
|
||||||
|
|
||||||
|
r = client.patch('/api/v1/users/me', json=data)
|
||||||
|
user = Users.query.filter_by(id=2).first()
|
||||||
|
assert verify_password(data['password'], user.password) is False
|
||||||
|
assert r.status_code == 400
|
||||||
|
assert r.get_json() == {
|
||||||
|
'errors': {
|
||||||
|
'confirm': ['Please confirm your current password']
|
||||||
|
},
|
||||||
|
'success': False
|
||||||
|
}
|
||||||
|
|
||||||
|
data['confirm'] = 'wrong_password'
|
||||||
|
|
||||||
|
r = client.patch('/api/v1/users/me', json=data)
|
||||||
|
user = Users.query.filter_by(id=2).first()
|
||||||
|
assert verify_password(data['password'], user.password) is False
|
||||||
|
assert r.status_code == 400
|
||||||
|
assert r.get_json() == {
|
||||||
|
'errors': {
|
||||||
|
'confirm': ['Your previous password is incorrect']
|
||||||
|
},
|
||||||
|
'success': False
|
||||||
|
}
|
||||||
|
|
||||||
|
data['confirm'] = 'password'
|
||||||
|
r = client.patch('/api/v1/users/me', json=data)
|
||||||
|
assert r.status_code == 200
|
||||||
|
user = Users.query.filter_by(id=2).first()
|
||||||
|
assert verify_password(data['password'], user.password) is True
|
||||||
|
destroy_ctfd(app)
|
||||||
@@ -124,34 +124,6 @@ def test_user_get_profile():
|
|||||||
destroy_ctfd(app)
|
destroy_ctfd(app)
|
||||||
|
|
||||||
|
|
||||||
def test_user_set_profile():
|
|
||||||
"""Can a registered user set their private profile (/profile)"""
|
|
||||||
app = create_ctfd()
|
|
||||||
with app.app_context():
|
|
||||||
register_user(app)
|
|
||||||
client = login_as_user(app)
|
|
||||||
r = client.get('/profile')
|
|
||||||
with client.session_transaction() as sess:
|
|
||||||
data = {
|
|
||||||
'name': 'user',
|
|
||||||
'email': 'user@ctfd.io',
|
|
||||||
# 'confirm': '',
|
|
||||||
# 'password': '',
|
|
||||||
'affiliation': 'affiliation_test',
|
|
||||||
'website': 'https://ctfd.io',
|
|
||||||
'country': 'US',
|
|
||||||
}
|
|
||||||
|
|
||||||
r = client.patch('/api/v1/users/me', json=data)
|
|
||||||
assert r.status_code == 200
|
|
||||||
|
|
||||||
user = Users.query.filter_by(id=2).first()
|
|
||||||
assert user.affiliation == 'affiliation_test'
|
|
||||||
assert user.website == 'https://ctfd.io'
|
|
||||||
assert user.country == 'US'
|
|
||||||
destroy_ctfd(app)
|
|
||||||
|
|
||||||
|
|
||||||
def test_user_can_access_files():
|
def test_user_can_access_files():
|
||||||
app = create_ctfd()
|
app = create_ctfd()
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
|
|||||||
11
tests/utils/test_validators.py
Normal file
11
tests/utils/test_validators.py
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
from CTFd.utils.validators import validate_country_code
|
||||||
|
from marshmallow import ValidationError
|
||||||
|
|
||||||
|
|
||||||
|
def test_validate_country_code():
|
||||||
|
assert validate_country_code('') is None
|
||||||
|
# TODO: This looks poor, when everything moves to pytest we should remove exception catches like this.
|
||||||
|
try:
|
||||||
|
validate_country_code('ZZ')
|
||||||
|
except ValidationError:
|
||||||
|
pass
|
||||||
Reference in New Issue
Block a user