Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-platform
Commits
7fe8e475
Commit
7fe8e475
authored
Nov 24, 2016
by
Vedran Karacic
Committed by
Marko Jevtic
Jan 31, 2017
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[SOL-2133] Add user deactivation endpoint.
parent
c2d7e446
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
240 additions
and
51 deletions
+240
-51
common/djangoapps/student/migrations/0009_auto_20170111_0422.py
+18
-0
common/djangoapps/student/models.py
+1
-0
common/djangoapps/student/tests/factories.py
+23
-1
openedx/core/djangoapps/user_api/accounts/permissions.py
+15
-0
openedx/core/djangoapps/user_api/accounts/tests/test_permissions.py
+40
-0
openedx/core/djangoapps/user_api/accounts/tests/test_views.py
+92
-27
openedx/core/djangoapps/user_api/accounts/views.py
+25
-2
openedx/core/djangoapps/user_api/preferences/tests/test_views.py
+20
-20
openedx/core/djangoapps/user_api/urls.py
+6
-1
No files found.
common/djangoapps/student/migrations/0009_auto_20170111_0422.py
0 → 100644
View file @
7fe8e475
# -*- coding: utf-8 -*-
from
__future__
import
unicode_literals
from
django.db
import
migrations
,
models
class
Migration
(
migrations
.
Migration
):
dependencies
=
[
(
'student'
,
'0008_auto_20161117_1209'
),
]
operations
=
[
migrations
.
AlterModelOptions
(
name
=
'userprofile'
,
options
=
{
'permissions'
:
((
'can_deactivate_users'
,
'Can deactivate, but NOT delete users'
),)},
),
]
common/djangoapps/student/models.py
View file @
7fe8e475
...
@@ -234,6 +234,7 @@ class UserProfile(models.Model):
...
@@ -234,6 +234,7 @@ class UserProfile(models.Model):
class
Meta
(
object
):
class
Meta
(
object
):
db_table
=
"auth_userprofile"
db_table
=
"auth_userprofile"
permissions
=
((
"can_deactivate_users"
,
"Can deactivate, but NOT delete users"
),)
# CRITICAL TODO/SECURITY
# CRITICAL TODO/SECURITY
# Sanitize all fields.
# Sanitize all fields.
...
...
common/djangoapps/student/tests/factories.py
View file @
7fe8e475
...
@@ -6,7 +6,8 @@ from student.models import (User, UserProfile, Registration,
...
@@ -6,7 +6,8 @@ from student.models import (User, UserProfile, Registration,
PendingEmailChange
,
UserStanding
,
PendingEmailChange
,
UserStanding
,
CourseAccessRole
)
CourseAccessRole
)
from
course_modes.models
import
CourseMode
from
course_modes.models
import
CourseMode
from
django.contrib.auth.models
import
Group
,
AnonymousUser
from
django.contrib.auth.models
import
AnonymousUser
,
Group
,
Permission
from
django.contrib.contenttypes.models
import
ContentType
from
datetime
import
datetime
from
datetime
import
datetime
import
factory
import
factory
from
factory
import
lazy_attribute
from
factory
import
lazy_attribute
...
@@ -18,6 +19,8 @@ from opaque_keys.edx.locations import SlashSeparatedCourseKey
...
@@ -18,6 +19,8 @@ from opaque_keys.edx.locations import SlashSeparatedCourseKey
# Factories are self documenting
# Factories are self documenting
# pylint: disable=missing-docstring
# pylint: disable=missing-docstring
TEST_PASSWORD
=
'test'
class
GroupFactory
(
DjangoModelFactory
):
class
GroupFactory
(
DjangoModelFactory
):
class
Meta
(
object
):
class
Meta
(
object
):
...
@@ -123,6 +126,10 @@ class AdminFactory(UserFactory):
...
@@ -123,6 +126,10 @@ class AdminFactory(UserFactory):
is_staff
=
True
is_staff
=
True
class
SuperuserFactory
(
UserFactory
):
is_superuser
=
True
class
CourseEnrollmentFactory
(
DjangoModelFactory
):
class
CourseEnrollmentFactory
(
DjangoModelFactory
):
class
Meta
(
object
):
class
Meta
(
object
):
model
=
CourseEnrollment
model
=
CourseEnrollment
...
@@ -161,3 +168,18 @@ class PendingEmailChangeFactory(DjangoModelFactory):
...
@@ -161,3 +168,18 @@ class PendingEmailChangeFactory(DjangoModelFactory):
user
=
factory
.
SubFactory
(
UserFactory
)
user
=
factory
.
SubFactory
(
UserFactory
)
new_email
=
factory
.
Sequence
(
u'new+email+{0}@edx.org'
.
format
)
new_email
=
factory
.
Sequence
(
u'new+email+{0}@edx.org'
.
format
)
activation_key
=
factory
.
Sequence
(
u'{:0<30d}'
.
format
)
activation_key
=
factory
.
Sequence
(
u'{:0<30d}'
.
format
)
class
ContentTypeFactory
(
DjangoModelFactory
):
class
Meta
(
object
):
model
=
ContentType
app_label
=
factory
.
Faker
(
'app_name'
)
class
PermissionFactory
(
DjangoModelFactory
):
class
Meta
(
object
):
model
=
Permission
codename
=
factory
.
Faker
(
'codename'
)
content_type
=
factory
.
SubFactory
(
ContentTypeFactory
)
openedx/core/djangoapps/user_api/accounts/permissions.py
0 → 100644
View file @
7fe8e475
"""
Permissions classes for User accounts API views.
"""
from
__future__
import
unicode_literals
from
rest_framework
import
permissions
class
CanDeactivateUser
(
permissions
.
BasePermission
):
"""
Grants access to AccountDeactivationView if the requesting user is a superuser
or has the explicit permission to deactivate a User account.
"""
def
has_permission
(
self
,
request
,
view
):
return
request
.
user
.
has_perm
(
'student.can_deactivate_users'
)
openedx/core/djangoapps/user_api/accounts/tests/test_permissions.py
0 → 100644
View file @
7fe8e475
"""
Tests for User deactivation API permissions
"""
from
django.test
import
TestCase
,
RequestFactory
from
openedx.core.djangoapps.user_api.accounts.permissions
import
CanDeactivateUser
from
student.tests.factories
import
ContentTypeFactory
,
PermissionFactory
,
SuperuserFactory
,
UserFactory
class
CanDeactivateUserTest
(
TestCase
):
""" Tests for user deactivation API permissions """
def
setUp
(
self
):
super
(
CanDeactivateUserTest
,
self
)
.
setUp
()
self
.
request
=
RequestFactory
()
.
get
(
'/test/url'
)
def
test_api_permission_superuser
(
self
):
self
.
request
.
user
=
SuperuserFactory
()
result
=
CanDeactivateUser
()
.
has_permission
(
self
.
request
,
None
)
self
.
assertTrue
(
result
)
def
test_api_permission_user_granted_permission
(
self
):
user
=
UserFactory
()
permission
=
PermissionFactory
(
codename
=
'can_deactivate_users'
,
content_type
=
ContentTypeFactory
(
app_label
=
'student'
)
)
user
.
user_permissions
.
add
(
permission
)
# pylint: disable=no-member
self
.
request
.
user
=
user
result
=
CanDeactivateUser
()
.
has_permission
(
self
.
request
,
None
)
self
.
assertTrue
(
result
)
def
test_api_permission_user_without_permission
(
self
):
self
.
request
.
user
=
UserFactory
()
result
=
CanDeactivateUser
()
.
has_permission
(
self
.
request
,
None
)
self
.
assertFalse
(
result
)
openedx/core/djangoapps/user_api/accounts/tests/test_views.py
View file @
7fe8e475
...
@@ -2,30 +2,35 @@
...
@@ -2,30 +2,35 @@
"""
"""
Test cases to cover Accounts-related behaviors of the User API application
Test cases to cover Accounts-related behaviors of the User API application
"""
"""
from
collections
import
OrderedDict
from
copy
import
deepcopy
import
datetime
import
datetime
import
ddt
import
ddt
import
hashlib
import
hashlib
import
json
import
json
import
unittest
from
collections
import
OrderedDict
from
copy
import
deepcopy
from
mock
import
patch
from
mock
import
patch
from
nose.plugins.attrib
import
attr
from
nose.plugins.attrib
import
attr
from
pytz
import
UTC
from
pytz
import
UTC
from
django.conf
import
settings
from
django.conf
import
settings
from
django.core.urlresolvers
import
reverse
from
django.core.urlresolvers
import
reverse
from
django.test
import
TestCase
from
django.test.testcases
import
TransactionTestCase
from
django.test.testcases
import
TransactionTestCase
from
django.test.utils
import
override_settings
from
django.test.utils
import
override_settings
from
rest_framework
import
status
from
rest_framework.test
import
APITestCase
,
APIClient
from
rest_framework.test
import
APITestCase
,
APIClient
from
openedx.core.djangoapps.user_api.models
import
UserPreference
from
student.tests.factories
import
UserFactory
from
..
import
PRIVATE_VISIBILITY
,
ALL_USERS_VISIBILITY
from
student.models
import
UserProfile
,
LanguageProficiency
,
PendingEmailChange
from
openedx.core.djangoapps.user_api.accounts
import
ACCOUNT_VISIBILITY_PREF_KEY
from
openedx.core.djangoapps.user_api.accounts
import
ACCOUNT_VISIBILITY_PREF_KEY
from
openedx.core.djangoapps.user_api.models
import
UserPreference
from
openedx.core.djangoapps.user_api.preferences.api
import
set_user_preference
from
openedx.core.djangoapps.user_api.preferences.api
import
set_user_preference
from
openedx.core.djangolib.testing.utils
import
CacheIsolationTestCase
,
skip_unless_lms
from
openedx.core.djangolib.testing.utils
import
CacheIsolationTestCase
,
skip_unless_lms
from
..
import
PRIVATE_VISIBILITY
,
ALL_USERS_VISIBILITY
from
student.models
import
UserProfile
,
LanguageProficiency
,
PendingEmailChange
from
student.tests.factories
import
(
AdminFactory
,
ContentTypeFactory
,
TEST_PASSWORD
,
PermissionFactory
,
SuperuserFactory
,
UserFactory
)
TEST_PROFILE_IMAGE_UPLOADED_AT
=
datetime
.
datetime
(
2002
,
1
,
9
,
15
,
43
,
01
,
tzinfo
=
UTC
)
TEST_PROFILE_IMAGE_UPLOADED_AT
=
datetime
.
datetime
(
2002
,
1
,
9
,
15
,
43
,
01
,
tzinfo
=
UTC
)
...
@@ -40,23 +45,22 @@ class UserAPITestCase(APITestCase):
...
@@ -40,23 +45,22 @@ class UserAPITestCase(APITestCase):
"""
"""
The base class for all tests of the User API
The base class for all tests of the User API
"""
"""
test_password
=
"test"
def
setUp
(
self
):
def
setUp
(
self
):
super
(
UserAPITestCase
,
self
)
.
setUp
()
super
(
UserAPITestCase
,
self
)
.
setUp
()
self
.
anonymous_client
=
APIClient
()
self
.
anonymous_client
=
APIClient
()
self
.
different_user
=
UserFactory
.
create
(
password
=
self
.
test_password
)
self
.
different_user
=
UserFactory
.
create
(
password
=
TEST_PASSWORD
)
self
.
different_client
=
APIClient
()
self
.
different_client
=
APIClient
()
self
.
staff_user
=
UserFactory
(
is_staff
=
True
,
password
=
self
.
test_password
)
self
.
staff_user
=
UserFactory
(
is_staff
=
True
,
password
=
TEST_PASSWORD
)
self
.
staff_client
=
APIClient
()
self
.
staff_client
=
APIClient
()
self
.
user
=
UserFactory
.
create
(
password
=
self
.
test_password
)
# will be assigned to self.client by default
self
.
user
=
UserFactory
.
create
(
password
=
TEST_PASSWORD
)
# will be assigned to self.client by default
def
login_client
(
self
,
api_client
,
user
):
def
login_client
(
self
,
api_client
,
user
):
"""Helper method for getting the client and user and logging in. Returns client. """
"""Helper method for getting the client and user and logging in. Returns client. """
client
=
getattr
(
self
,
api_client
)
client
=
getattr
(
self
,
api_client
)
user
=
getattr
(
self
,
user
)
user
=
getattr
(
self
,
user
)
client
.
login
(
username
=
user
.
username
,
password
=
self
.
test_password
)
client
.
login
(
username
=
user
.
username
,
password
=
TEST_PASSWORD
)
return
client
return
client
def
send_patch
(
self
,
client
,
json_data
,
content_type
=
"application/merge-patch+json"
,
expected_status
=
200
):
def
send_patch
(
self
,
client
,
json_data
,
content_type
=
"application/merge-patch+json"
,
expected_status
=
200
):
...
@@ -168,7 +172,7 @@ class TestOwnUsernameAPI(CacheIsolationTestCase, UserAPITestCase):
...
@@ -168,7 +172,7 @@ class TestOwnUsernameAPI(CacheIsolationTestCase, UserAPITestCase):
"""
"""
Test that a client (logged in) can get her own username.
Test that a client (logged in) can get her own username.
"""
"""
self
.
client
.
login
(
username
=
self
.
user
.
username
,
password
=
self
.
test_password
)
self
.
client
.
login
(
username
=
self
.
user
.
username
,
password
=
TEST_PASSWORD
)
self
.
_verify_get_own_username
(
15
)
self
.
_verify_get_own_username
(
15
)
def
test_get_username_inactive
(
self
):
def
test_get_username_inactive
(
self
):
...
@@ -176,7 +180,7 @@ class TestOwnUsernameAPI(CacheIsolationTestCase, UserAPITestCase):
...
@@ -176,7 +180,7 @@ class TestOwnUsernameAPI(CacheIsolationTestCase, UserAPITestCase):
Test that a logged-in client can get their
Test that a logged-in client can get their
username, even if inactive.
username, even if inactive.
"""
"""
self
.
client
.
login
(
username
=
self
.
user
.
username
,
password
=
self
.
test_password
)
self
.
client
.
login
(
username
=
self
.
user
.
username
,
password
=
TEST_PASSWORD
)
self
.
user
.
is_active
=
False
self
.
user
.
is_active
=
False
self
.
user
.
save
()
self
.
user
.
save
()
self
.
_verify_get_own_username
(
15
)
self
.
_verify_get_own_username
(
15
)
...
@@ -271,7 +275,7 @@ class TestAccountsAPI(CacheIsolationTestCase, UserAPITestCase):
...
@@ -271,7 +275,7 @@ class TestAccountsAPI(CacheIsolationTestCase, UserAPITestCase):
"""
"""
Test that DELETE, POST, and PUT are not supported.
Test that DELETE, POST, and PUT are not supported.
"""
"""
self
.
client
.
login
(
username
=
self
.
user
.
username
,
password
=
self
.
test_password
)
self
.
client
.
login
(
username
=
self
.
user
.
username
,
password
=
TEST_PASSWORD
)
self
.
assertEqual
(
405
,
self
.
client
.
put
(
self
.
url
)
.
status_code
)
self
.
assertEqual
(
405
,
self
.
client
.
put
(
self
.
url
)
.
status_code
)
self
.
assertEqual
(
405
,
self
.
client
.
post
(
self
.
url
)
.
status_code
)
self
.
assertEqual
(
405
,
self
.
client
.
post
(
self
.
url
)
.
status_code
)
self
.
assertEqual
(
405
,
self
.
client
.
delete
(
self
.
url
)
.
status_code
)
self
.
assertEqual
(
405
,
self
.
client
.
delete
(
self
.
url
)
.
status_code
)
...
@@ -298,7 +302,7 @@ class TestAccountsAPI(CacheIsolationTestCase, UserAPITestCase):
...
@@ -298,7 +302,7 @@ class TestAccountsAPI(CacheIsolationTestCase, UserAPITestCase):
Test that a client (logged in) can only get the shareable fields for a different user.
Test that a client (logged in) can only get the shareable fields for a different user.
This is the case when default_visibility is set to "all_users".
This is the case when default_visibility is set to "all_users".
"""
"""
self
.
different_client
.
login
(
username
=
self
.
different_user
.
username
,
password
=
self
.
test_password
)
self
.
different_client
.
login
(
username
=
self
.
different_user
.
username
,
password
=
TEST_PASSWORD
)
self
.
create_mock_profile
(
self
.
user
)
self
.
create_mock_profile
(
self
.
user
)
with
self
.
assertNumQueries
(
19
):
with
self
.
assertNumQueries
(
19
):
response
=
self
.
send_get
(
self
.
different_client
)
response
=
self
.
send_get
(
self
.
different_client
)
...
@@ -313,7 +317,7 @@ class TestAccountsAPI(CacheIsolationTestCase, UserAPITestCase):
...
@@ -313,7 +317,7 @@ class TestAccountsAPI(CacheIsolationTestCase, UserAPITestCase):
Test that a client (logged in) can only get the shareable fields for a different user.
Test that a client (logged in) can only get the shareable fields for a different user.
This is the case when default_visibility is set to "private".
This is the case when default_visibility is set to "private".
"""
"""
self
.
different_client
.
login
(
username
=
self
.
different_user
.
username
,
password
=
self
.
test_password
)
self
.
different_client
.
login
(
username
=
self
.
different_user
.
username
,
password
=
TEST_PASSWORD
)
self
.
create_mock_profile
(
self
.
user
)
self
.
create_mock_profile
(
self
.
user
)
with
self
.
assertNumQueries
(
19
):
with
self
.
assertNumQueries
(
19
):
response
=
self
.
send_get
(
self
.
different_client
)
response
=
self
.
send_get
(
self
.
different_client
)
...
@@ -389,7 +393,7 @@ class TestAccountsAPI(CacheIsolationTestCase, UserAPITestCase):
...
@@ -389,7 +393,7 @@ class TestAccountsAPI(CacheIsolationTestCase, UserAPITestCase):
# Badges aren't on by default, so should not be present.
# Badges aren't on by default, so should not be present.
self
.
assertEqual
(
False
,
data
[
"accomplishments_shared"
])
self
.
assertEqual
(
False
,
data
[
"accomplishments_shared"
])
self
.
client
.
login
(
username
=
self
.
user
.
username
,
password
=
self
.
test_password
)
self
.
client
.
login
(
username
=
self
.
user
.
username
,
password
=
TEST_PASSWORD
)
verify_get_own_information
(
17
)
verify_get_own_information
(
17
)
# Now make sure that the user can get the same information, even if not active
# Now make sure that the user can get the same information, even if not active
...
@@ -408,7 +412,7 @@ class TestAccountsAPI(CacheIsolationTestCase, UserAPITestCase):
...
@@ -408,7 +412,7 @@ class TestAccountsAPI(CacheIsolationTestCase, UserAPITestCase):
legacy_profile
.
bio
=
""
legacy_profile
.
bio
=
""
legacy_profile
.
save
()
legacy_profile
.
save
()
self
.
client
.
login
(
username
=
self
.
user
.
username
,
password
=
self
.
test_password
)
self
.
client
.
login
(
username
=
self
.
user
.
username
,
password
=
TEST_PASSWORD
)
with
self
.
assertNumQueries
(
17
):
with
self
.
assertNumQueries
(
17
):
response
=
self
.
send_get
(
self
.
client
)
response
=
self
.
send_get
(
self
.
client
)
for
empty_field
in
(
"level_of_education"
,
"gender"
,
"country"
,
"bio"
):
for
empty_field
in
(
"level_of_education"
,
"gender"
,
"country"
,
"bio"
):
...
@@ -499,7 +503,7 @@ class TestAccountsAPI(CacheIsolationTestCase, UserAPITestCase):
...
@@ -499,7 +503,7 @@ class TestAccountsAPI(CacheIsolationTestCase, UserAPITestCase):
def
test_patch_inactive_user
(
self
):
def
test_patch_inactive_user
(
self
):
""" Verify that a user can patch her own account, even if inactive. """
""" Verify that a user can patch her own account, even if inactive. """
self
.
client
.
login
(
username
=
self
.
user
.
username
,
password
=
self
.
test_password
)
self
.
client
.
login
(
username
=
self
.
user
.
username
,
password
=
TEST_PASSWORD
)
self
.
user
.
is_active
=
False
self
.
user
.
is_active
=
False
self
.
user
.
save
()
self
.
user
.
save
()
response
=
self
.
send_patch
(
self
.
client
,
{
"goals"
:
"to not activate account"
})
response
=
self
.
send_patch
(
self
.
client
,
{
"goals"
:
"to not activate account"
})
...
@@ -541,7 +545,7 @@ class TestAccountsAPI(CacheIsolationTestCase, UserAPITestCase):
...
@@ -541,7 +545,7 @@ class TestAccountsAPI(CacheIsolationTestCase, UserAPITestCase):
"""
"""
Test the behavior of patch when an incorrect content_type is specified.
Test the behavior of patch when an incorrect content_type is specified.
"""
"""
self
.
client
.
login
(
username
=
self
.
user
.
username
,
password
=
self
.
test_password
)
self
.
client
.
login
(
username
=
self
.
user
.
username
,
password
=
TEST_PASSWORD
)
self
.
send_patch
(
self
.
client
,
{},
content_type
=
"application/json"
,
expected_status
=
415
)
self
.
send_patch
(
self
.
client
,
{},
content_type
=
"application/json"
,
expected_status
=
415
)
self
.
send_patch
(
self
.
client
,
{},
content_type
=
"application/xml"
,
expected_status
=
415
)
self
.
send_patch
(
self
.
client
,
{},
content_type
=
"application/xml"
,
expected_status
=
415
)
...
@@ -550,7 +554,7 @@ class TestAccountsAPI(CacheIsolationTestCase, UserAPITestCase):
...
@@ -550,7 +554,7 @@ class TestAccountsAPI(CacheIsolationTestCase, UserAPITestCase):
Tests the behavior of patch when attempting to set fields with a select list of options to the empty string.
Tests the behavior of patch when attempting to set fields with a select list of options to the empty string.
Also verifies the behaviour when setting to None.
Also verifies the behaviour when setting to None.
"""
"""
self
.
client
.
login
(
username
=
self
.
user
.
username
,
password
=
self
.
test_password
)
self
.
client
.
login
(
username
=
self
.
user
.
username
,
password
=
TEST_PASSWORD
)
for
field_name
in
[
"gender"
,
"level_of_education"
,
"country"
]:
for
field_name
in
[
"gender"
,
"level_of_education"
,
"country"
]:
response
=
self
.
send_patch
(
self
.
client
,
{
field_name
:
""
})
response
=
self
.
send_patch
(
self
.
client
,
{
field_name
:
""
})
# Although throwing a 400 might be reasonable, the default DRF behavior with ModelSerializer
# Although throwing a 400 might be reasonable, the default DRF behavior with ModelSerializer
...
@@ -586,7 +590,7 @@ class TestAccountsAPI(CacheIsolationTestCase, UserAPITestCase):
...
@@ -586,7 +590,7 @@ class TestAccountsAPI(CacheIsolationTestCase, UserAPITestCase):
get_response
=
self
.
send_get
(
self
.
client
)
get_response
=
self
.
send_get
(
self
.
client
)
self
.
assertEqual
(
new_name
,
get_response
.
data
[
"name"
])
self
.
assertEqual
(
new_name
,
get_response
.
data
[
"name"
])
self
.
client
.
login
(
username
=
self
.
user
.
username
,
password
=
self
.
test_password
)
self
.
client
.
login
(
username
=
self
.
user
.
username
,
password
=
TEST_PASSWORD
)
legacy_profile
=
UserProfile
.
objects
.
get
(
id
=
self
.
user
.
id
)
legacy_profile
=
UserProfile
.
objects
.
get
(
id
=
self
.
user
.
id
)
self
.
assertEqual
({},
legacy_profile
.
get_meta
())
self
.
assertEqual
({},
legacy_profile
.
get_meta
())
old_name
=
legacy_profile
.
name
old_name
=
legacy_profile
.
name
...
@@ -706,7 +710,7 @@ class TestAccountsAPI(CacheIsolationTestCase, UserAPITestCase):
...
@@ -706,7 +710,7 @@ class TestAccountsAPI(CacheIsolationTestCase, UserAPITestCase):
Test that AccountUpdateErrors are passed through to the response.
Test that AccountUpdateErrors are passed through to the response.
"""
"""
serializer_save
.
side_effect
=
[
Exception
(
"bummer"
),
None
]
serializer_save
.
side_effect
=
[
Exception
(
"bummer"
),
None
]
self
.
client
.
login
(
username
=
self
.
user
.
username
,
password
=
self
.
test_password
)
self
.
client
.
login
(
username
=
self
.
user
.
username
,
password
=
TEST_PASSWORD
)
error_response
=
self
.
send_patch
(
self
.
client
,
{
"goals"
:
"save an account field"
},
expected_status
=
400
)
error_response
=
self
.
send_patch
(
self
.
client
,
{
"goals"
:
"save an account field"
},
expected_status
=
400
)
self
.
assertEqual
(
self
.
assertEqual
(
"Error thrown when saving account updates: 'bummer'"
,
"Error thrown when saving account updates: 'bummer'"
,
...
@@ -721,7 +725,7 @@ class TestAccountsAPI(CacheIsolationTestCase, UserAPITestCase):
...
@@ -721,7 +725,7 @@ class TestAccountsAPI(CacheIsolationTestCase, UserAPITestCase):
with a '/', the API generates the full URL to profile images based on
with a '/', the API generates the full URL to profile images based on
the URL of the request.
the URL of the request.
"""
"""
self
.
client
.
login
(
username
=
self
.
user
.
username
,
password
=
self
.
test_password
)
self
.
client
.
login
(
username
=
self
.
user
.
username
,
password
=
TEST_PASSWORD
)
response
=
self
.
send_get
(
self
.
client
)
response
=
self
.
send_get
(
self
.
client
)
self
.
assertEqual
(
self
.
assertEqual
(
response
.
data
[
"profile_image"
],
response
.
data
[
"profile_image"
],
...
@@ -787,12 +791,11 @@ class TestAccountAPITransactions(TransactionTestCase):
...
@@ -787,12 +791,11 @@ class TestAccountAPITransactions(TransactionTestCase):
"""
"""
Tests the transactional behavior of the account API
Tests the transactional behavior of the account API
"""
"""
test_password
=
"test"
def
setUp
(
self
):
def
setUp
(
self
):
super
(
TestAccountAPITransactions
,
self
)
.
setUp
()
super
(
TestAccountAPITransactions
,
self
)
.
setUp
()
self
.
client
=
APIClient
()
self
.
client
=
APIClient
()
self
.
user
=
UserFactory
.
create
(
password
=
self
.
test_password
)
self
.
user
=
UserFactory
.
create
(
password
=
TEST_PASSWORD
)
self
.
url
=
reverse
(
"accounts_api"
,
kwargs
=
{
'username'
:
self
.
user
.
username
})
self
.
url
=
reverse
(
"accounts_api"
,
kwargs
=
{
'username'
:
self
.
user
.
username
})
@patch
(
'student.views.do_email_change_request'
)
@patch
(
'student.views.do_email_change_request'
)
...
@@ -804,7 +807,7 @@ class TestAccountAPITransactions(TransactionTestCase):
...
@@ -804,7 +807,7 @@ class TestAccountAPITransactions(TransactionTestCase):
# Throw an error from the method that is used to process the email change request
# Throw an error from the method that is used to process the email change request
# (this is the last thing done in the api method). Verify that the profile did not change.
# (this is the last thing done in the api method). Verify that the profile did not change.
mock_email_change
.
side_effect
=
[
ValueError
,
"mock value error thrown"
]
mock_email_change
.
side_effect
=
[
ValueError
,
"mock value error thrown"
]
self
.
client
.
login
(
username
=
self
.
user
.
username
,
password
=
self
.
test_password
)
self
.
client
.
login
(
username
=
self
.
user
.
username
,
password
=
TEST_PASSWORD
)
old_email
=
self
.
user
.
email
old_email
=
self
.
user
.
email
json_data
=
{
"email"
:
"foo@bar.com"
,
"gender"
:
"o"
}
json_data
=
{
"email"
:
"foo@bar.com"
,
"gender"
:
"o"
}
...
@@ -816,3 +819,65 @@ class TestAccountAPITransactions(TransactionTestCase):
...
@@ -816,3 +819,65 @@ class TestAccountAPITransactions(TransactionTestCase):
data
=
response
.
data
data
=
response
.
data
self
.
assertEqual
(
old_email
,
data
[
"email"
])
self
.
assertEqual
(
old_email
,
data
[
"email"
])
self
.
assertEqual
(
u"m"
,
data
[
"gender"
])
self
.
assertEqual
(
u"m"
,
data
[
"gender"
])
@unittest.skipUnless
(
settings
.
ROOT_URLCONF
==
'lms.urls'
,
'Account APIs are only supported in LMS'
)
class
TestAccountDeactivation
(
TestCase
):
"""
Tests the account deactivation endpoint.
"""
def
setUp
(
self
):
super
(
TestAccountDeactivation
,
self
)
.
setUp
()
self
.
superuser
=
SuperuserFactory
()
self
.
staff_user
=
AdminFactory
()
self
.
test_user
=
UserFactory
()
self
.
url
=
reverse
(
'accounts_deactivation'
,
kwargs
=
{
'username'
:
self
.
test_user
.
username
})
def
assert_activation_status
(
self
,
expected_status
=
status
.
HTTP_200_OK
,
expected_activation_status
=
False
):
"""
Helper function for making a request to the deactivation endpoint, and asserting the status.
Args:
expected_status(int): Expected request's response status.
expected_activation_status(bool): Expected user has_usable_password attribute value.
"""
response
=
self
.
client
.
post
(
self
.
url
)
self
.
assertEqual
(
response
.
status_code
,
expected_status
)
self
.
test_user
.
refresh_from_db
()
# pylint: disable=no-member
self
.
assertEqual
(
self
.
test_user
.
has_usable_password
(),
expected_activation_status
)
# pylint: disable=no-member
def
test_superuser_deactivates_user
(
self
):
"""
Verify a user is deactivated when a superuser posts to the deactivation endpoint.
"""
self
.
client
.
login
(
username
=
self
.
superuser
.
username
,
password
=
TEST_PASSWORD
)
self
.
assertTrue
(
self
.
test_user
.
has_usable_password
())
# pylint: disable=no-member
self
.
assert_activation_status
()
def
test_user_with_permission_deactivates_user
(
self
):
"""
Verify a user is deactivated when a user with permission posts to the deactivation endpoint.
"""
user
=
UserFactory
()
permission
=
PermissionFactory
(
codename
=
'can_deactivate_users'
,
content_type
=
ContentTypeFactory
(
app_label
=
'student'
)
)
user
.
user_permissions
.
add
(
permission
)
# pylint: disable=no-member
self
.
client
.
login
(
username
=
user
.
username
,
password
=
TEST_PASSWORD
)
self
.
assertTrue
(
self
.
test_user
.
has_usable_password
())
# pylint: disable=no-member
self
.
assert_activation_status
()
def
test_unauthorized_rejection
(
self
):
"""
Verify unauthorized users cannot deactivate accounts.
"""
self
.
client
.
login
(
username
=
self
.
test_user
.
username
,
password
=
TEST_PASSWORD
)
self
.
assertTrue
(
self
.
test_user
.
has_usable_password
())
# pylint: disable=no-member
self
.
assert_activation_status
(
expected_status
=
status
.
HTTP_403_FORBIDDEN
,
expected_activation_status
=
True
)
openedx/core/djangoapps/user_api/accounts/views.py
View file @
7fe8e475
...
@@ -10,15 +10,18 @@ from edx_rest_framework_extensions.authentication import JwtAuthentication
...
@@ -10,15 +10,18 @@ from edx_rest_framework_extensions.authentication import JwtAuthentication
from
rest_framework
import
permissions
from
rest_framework
import
permissions
from
rest_framework
import
status
from
rest_framework
import
status
from
rest_framework.response
import
Response
from
rest_framework.response
import
Response
from
rest_framework.views
import
APIView
from
rest_framework.viewsets
import
ViewSet
from
rest_framework.viewsets
import
ViewSet
from
.api
import
get_account_settings
,
update_account_settings
from
.permissions
import
CanDeactivateUser
from
..errors
import
UserNotFound
,
UserNotAuthorized
,
AccountUpdateError
,
AccountValidationError
from
openedx.core.lib.api.authentication
import
(
from
openedx.core.lib.api.authentication
import
(
SessionAuthenticationAllowInactiveUser
,
SessionAuthenticationAllowInactiveUser
,
OAuth2AuthenticationAllowInactiveUser
,
OAuth2AuthenticationAllowInactiveUser
,
)
)
from
openedx.core.lib.api.parsers
import
MergePatchParser
from
openedx.core.lib.api.parsers
import
MergePatchParser
from
.api
import
get_account_settings
,
update_account_settings
from
student.models
import
User
from
..errors
import
UserNotFound
,
UserNotAuthorized
,
AccountUpdateError
,
AccountValidationError
class
AccountViewSet
(
ViewSet
):
class
AccountViewSet
(
ViewSet
):
...
@@ -219,3 +222,23 @@ class AccountViewSet(ViewSet):
...
@@ -219,3 +222,23 @@ class AccountViewSet(ViewSet):
)
)
return
Response
(
account_settings
)
return
Response
(
account_settings
)
class
AccountDeactivationView
(
APIView
):
"""
Account deactivation viewset. Currently only supports POST requests.
Only admins can deactivate accounts.
"""
permission_classes
=
(
permissions
.
IsAuthenticated
,
CanDeactivateUser
)
def
post
(
self
,
request
,
username
):
"""
POST /api/user/v1/accounts/{username}/deactivate/
Marks the user as having no password set for deactivation purposes.
"""
user
=
User
.
objects
.
get
(
username
=
username
)
user
.
set_unusable_password
()
user
.
save
()
account_settings
=
get_account_settings
(
request
,
[
username
])[
0
]
return
Response
(
account_settings
)
openedx/core/djangoapps/user_api/preferences/tests/test_views.py
View file @
7fe8e475
...
@@ -10,7 +10,7 @@ from mock import patch
...
@@ -10,7 +10,7 @@ from mock import patch
from
django.core.urlresolvers
import
reverse
from
django.core.urlresolvers
import
reverse
from
django.test.testcases
import
TransactionTestCase
from
django.test.testcases
import
TransactionTestCase
from
rest_framework.test
import
APIClient
from
rest_framework.test
import
APIClient
from
student.tests.factories
import
UserFactory
from
student.tests.factories
import
UserFactory
,
TEST_PASSWORD
from
openedx.core.djangolib.testing.utils
import
skip_unless_lms
from
openedx.core.djangolib.testing.utils
import
skip_unless_lms
from
...accounts.tests.test_views
import
UserAPITestCase
from
...accounts.tests.test_views
import
UserAPITestCase
...
@@ -42,7 +42,7 @@ class TestPreferencesAPI(UserAPITestCase):
...
@@ -42,7 +42,7 @@ class TestPreferencesAPI(UserAPITestCase):
"""
"""
Test that DELETE, POST, and PUT are not supported.
Test that DELETE, POST, and PUT are not supported.
"""
"""
self
.
client
.
login
(
username
=
self
.
user
.
username
,
password
=
self
.
test_password
)
self
.
client
.
login
(
username
=
self
.
user
.
username
,
password
=
TEST_PASSWORD
)
self
.
assertEqual
(
405
,
self
.
client
.
put
(
self
.
url
)
.
status_code
)
self
.
assertEqual
(
405
,
self
.
client
.
put
(
self
.
url
)
.
status_code
)
self
.
assertEqual
(
405
,
self
.
client
.
post
(
self
.
url
)
.
status_code
)
self
.
assertEqual
(
405
,
self
.
client
.
post
(
self
.
url
)
.
status_code
)
self
.
assertEqual
(
405
,
self
.
client
.
delete
(
self
.
url
)
.
status_code
)
self
.
assertEqual
(
405
,
self
.
client
.
delete
(
self
.
url
)
.
status_code
)
...
@@ -51,7 +51,7 @@ class TestPreferencesAPI(UserAPITestCase):
...
@@ -51,7 +51,7 @@ class TestPreferencesAPI(UserAPITestCase):
"""
"""
Test that a client (logged in) cannot get the preferences information for a different client.
Test that a client (logged in) cannot get the preferences information for a different client.
"""
"""
self
.
different_client
.
login
(
username
=
self
.
different_user
.
username
,
password
=
self
.
test_password
)
self
.
different_client
.
login
(
username
=
self
.
different_user
.
username
,
password
=
TEST_PASSWORD
)
self
.
send_get
(
self
.
different_client
,
expected_status
=
404
)
self
.
send_get
(
self
.
different_client
,
expected_status
=
404
)
@ddt.data
(
@ddt.data
(
...
@@ -72,7 +72,7 @@ class TestPreferencesAPI(UserAPITestCase):
...
@@ -72,7 +72,7 @@ class TestPreferencesAPI(UserAPITestCase):
Test that a client (logged in) can get her own preferences information (verifying the default
Test that a client (logged in) can get her own preferences information (verifying the default
state before any preferences are stored).
state before any preferences are stored).
"""
"""
self
.
client
.
login
(
username
=
self
.
user
.
username
,
password
=
self
.
test_password
)
self
.
client
.
login
(
username
=
self
.
user
.
username
,
password
=
TEST_PASSWORD
)
response
=
self
.
send_get
(
self
.
client
)
response
=
self
.
send_get
(
self
.
client
)
self
.
assertEqual
({},
response
.
data
)
self
.
assertEqual
({},
response
.
data
)
...
@@ -117,7 +117,7 @@ class TestPreferencesAPI(UserAPITestCase):
...
@@ -117,7 +117,7 @@ class TestPreferencesAPI(UserAPITestCase):
"""
"""
Test the behavior of patch when an incorrect content_type is specified.
Test the behavior of patch when an incorrect content_type is specified.
"""
"""
self
.
client
.
login
(
username
=
self
.
user
.
username
,
password
=
self
.
test_password
)
self
.
client
.
login
(
username
=
self
.
user
.
username
,
password
=
TEST_PASSWORD
)
self
.
send_patch
(
self
.
client
,
{},
content_type
=
"application/json"
,
expected_status
=
415
)
self
.
send_patch
(
self
.
client
,
{},
content_type
=
"application/json"
,
expected_status
=
415
)
self
.
send_patch
(
self
.
client
,
{},
content_type
=
"application/xml"
,
expected_status
=
415
)
self
.
send_patch
(
self
.
client
,
{},
content_type
=
"application/xml"
,
expected_status
=
415
)
...
@@ -137,7 +137,7 @@ class TestPreferencesAPI(UserAPITestCase):
...
@@ -137,7 +137,7 @@ class TestPreferencesAPI(UserAPITestCase):
"""
"""
Internal helper to generalize the creation of a set of preferences
Internal helper to generalize the creation of a set of preferences
"""
"""
self
.
client
.
login
(
username
=
self
.
user
.
username
,
password
=
self
.
test_password
)
self
.
client
.
login
(
username
=
self
.
user
.
username
,
password
=
TEST_PASSWORD
)
if
not
is_active
:
if
not
is_active
:
self
.
user
.
is_active
=
False
self
.
user
.
is_active
=
False
self
.
user
.
save
()
self
.
user
.
save
()
...
@@ -182,7 +182,7 @@ class TestPreferencesAPI(UserAPITestCase):
...
@@ -182,7 +182,7 @@ class TestPreferencesAPI(UserAPITestCase):
set_user_preference
(
self
.
user
,
"time_zone"
,
"Asia/Macau"
)
set_user_preference
(
self
.
user
,
"time_zone"
,
"Asia/Macau"
)
# Send the patch request
# Send the patch request
self
.
client
.
login
(
username
=
self
.
user
.
username
,
password
=
self
.
test_password
)
self
.
client
.
login
(
username
=
self
.
user
.
username
,
password
=
TEST_PASSWORD
)
self
.
send_patch
(
self
.
send_patch
(
self
.
client
,
self
.
client
,
{
{
...
@@ -215,7 +215,7 @@ class TestPreferencesAPI(UserAPITestCase):
...
@@ -215,7 +215,7 @@ class TestPreferencesAPI(UserAPITestCase):
set_user_preference
(
self
.
user
,
"time_zone"
,
"Pacific/Midway"
)
set_user_preference
(
self
.
user
,
"time_zone"
,
"Pacific/Midway"
)
# Send the patch request
# Send the patch request
self
.
client
.
login
(
username
=
self
.
user
.
username
,
password
=
self
.
test_password
)
self
.
client
.
login
(
username
=
self
.
user
.
username
,
password
=
TEST_PASSWORD
)
response
=
self
.
send_patch
(
response
=
self
.
send_patch
(
self
.
client
,
self
.
client
,
{
{
...
@@ -266,7 +266,7 @@ class TestPreferencesAPI(UserAPITestCase):
...
@@ -266,7 +266,7 @@ class TestPreferencesAPI(UserAPITestCase):
"""
"""
Test that a client (logged in) receives appropriate errors for a bad request.
Test that a client (logged in) receives appropriate errors for a bad request.
"""
"""
self
.
client
.
login
(
username
=
self
.
user
.
username
,
password
=
self
.
test_password
)
self
.
client
.
login
(
username
=
self
.
user
.
username
,
password
=
TEST_PASSWORD
)
# Verify a non-dict request
# Verify a non-dict request
response
=
self
.
send_patch
(
self
.
client
,
"non_dict_request"
,
expected_status
=
400
)
response
=
self
.
send_patch
(
self
.
client
,
"non_dict_request"
,
expected_status
=
400
)
...
@@ -325,7 +325,7 @@ class TestPreferencesAPITransactions(TransactionTestCase):
...
@@ -325,7 +325,7 @@ class TestPreferencesAPITransactions(TransactionTestCase):
def
setUp
(
self
):
def
setUp
(
self
):
super
(
TestPreferencesAPITransactions
,
self
)
.
setUp
()
super
(
TestPreferencesAPITransactions
,
self
)
.
setUp
()
self
.
client
=
APIClient
()
self
.
client
=
APIClient
()
self
.
user
=
UserFactory
.
create
(
password
=
self
.
test_password
)
self
.
user
=
UserFactory
.
create
(
password
=
TEST_PASSWORD
)
self
.
url
=
reverse
(
"preferences_api"
,
kwargs
=
{
'username'
:
self
.
user
.
username
})
self
.
url
=
reverse
(
"preferences_api"
,
kwargs
=
{
'username'
:
self
.
user
.
username
})
@patch
(
'openedx.core.djangoapps.user_api.models.UserPreference.delete'
)
@patch
(
'openedx.core.djangoapps.user_api.models.UserPreference.delete'
)
...
@@ -342,7 +342,7 @@ class TestPreferencesAPITransactions(TransactionTestCase):
...
@@ -342,7 +342,7 @@ class TestPreferencesAPITransactions(TransactionTestCase):
# after one of the updates has happened, in which case the whole operation
# after one of the updates has happened, in which case the whole operation
# should be rolled back.
# should be rolled back.
delete_user_preference
.
side_effect
=
[
Exception
,
None
]
delete_user_preference
.
side_effect
=
[
Exception
,
None
]
self
.
client
.
login
(
username
=
self
.
user
.
username
,
password
=
self
.
test_password
)
self
.
client
.
login
(
username
=
self
.
user
.
username
,
password
=
TEST_PASSWORD
)
json_data
=
{
json_data
=
{
"a"
:
"2"
,
"a"
:
"2"
,
"b"
:
None
,
"b"
:
None
,
...
@@ -396,7 +396,7 @@ class TestPreferencesDetailAPI(UserAPITestCase):
...
@@ -396,7 +396,7 @@ class TestPreferencesDetailAPI(UserAPITestCase):
"""
"""
Test that POST and PATCH are not supported.
Test that POST and PATCH are not supported.
"""
"""
self
.
client
.
login
(
username
=
self
.
user
.
username
,
password
=
self
.
test_password
)
self
.
client
.
login
(
username
=
self
.
user
.
username
,
password
=
TEST_PASSWORD
)
self
.
assertEqual
(
405
,
self
.
client
.
post
(
self
.
url
)
.
status_code
)
self
.
assertEqual
(
405
,
self
.
client
.
post
(
self
.
url
)
.
status_code
)
self
.
assertEqual
(
405
,
self
.
client
.
patch
(
self
.
url
)
.
status_code
)
self
.
assertEqual
(
405
,
self
.
client
.
patch
(
self
.
url
)
.
status_code
)
...
@@ -404,7 +404,7 @@ class TestPreferencesDetailAPI(UserAPITestCase):
...
@@ -404,7 +404,7 @@ class TestPreferencesDetailAPI(UserAPITestCase):
"""
"""
Test that a client (logged in) cannot manipulate a preference for a different client.
Test that a client (logged in) cannot manipulate a preference for a different client.
"""
"""
self
.
different_client
.
login
(
username
=
self
.
different_user
.
username
,
password
=
self
.
test_password
)
self
.
different_client
.
login
(
username
=
self
.
different_user
.
username
,
password
=
TEST_PASSWORD
)
self
.
send_get
(
self
.
different_client
,
expected_status
=
404
)
self
.
send_get
(
self
.
different_client
,
expected_status
=
404
)
self
.
send_put
(
self
.
different_client
,
"new_value"
,
expected_status
=
404
)
self
.
send_put
(
self
.
different_client
,
"new_value"
,
expected_status
=
404
)
self
.
send_delete
(
self
.
different_client
,
expected_status
=
404
)
self
.
send_delete
(
self
.
different_client
,
expected_status
=
404
)
...
@@ -429,7 +429,7 @@ class TestPreferencesDetailAPI(UserAPITestCase):
...
@@ -429,7 +429,7 @@ class TestPreferencesDetailAPI(UserAPITestCase):
Test that a 404 is returned if the user does not have a preference with the given preference_key.
Test that a 404 is returned if the user does not have a preference with the given preference_key.
"""
"""
self
.
_set_url
(
"does_not_exist"
)
self
.
_set_url
(
"does_not_exist"
)
self
.
client
.
login
(
username
=
self
.
user
.
username
,
password
=
self
.
test_password
)
self
.
client
.
login
(
username
=
self
.
user
.
username
,
password
=
TEST_PASSWORD
)
response
=
self
.
send_get
(
self
.
client
,
expected_status
=
404
)
response
=
self
.
send_get
(
self
.
client
,
expected_status
=
404
)
self
.
assertIsNone
(
response
.
data
)
self
.
assertIsNone
(
response
.
data
)
...
@@ -469,7 +469,7 @@ class TestPreferencesDetailAPI(UserAPITestCase):
...
@@ -469,7 +469,7 @@ class TestPreferencesDetailAPI(UserAPITestCase):
"""
"""
Generalization of the actual test workflow
Generalization of the actual test workflow
"""
"""
self
.
client
.
login
(
username
=
self
.
user
.
username
,
password
=
self
.
test_password
)
self
.
client
.
login
(
username
=
self
.
user
.
username
,
password
=
TEST_PASSWORD
)
if
not
is_active
:
if
not
is_active
:
self
.
user
.
is_active
=
False
self
.
user
.
is_active
=
False
self
.
user
.
save
()
self
.
user
.
save
()
...
@@ -490,7 +490,7 @@ class TestPreferencesDetailAPI(UserAPITestCase):
...
@@ -490,7 +490,7 @@ class TestPreferencesDetailAPI(UserAPITestCase):
Test that a client (logged in) cannot create an empty preference.
Test that a client (logged in) cannot create an empty preference.
"""
"""
self
.
_set_url
(
"new_key"
)
self
.
_set_url
(
"new_key"
)
self
.
client
.
login
(
username
=
self
.
user
.
username
,
password
=
self
.
test_password
)
self
.
client
.
login
(
username
=
self
.
user
.
username
,
password
=
TEST_PASSWORD
)
response
=
self
.
send_put
(
self
.
client
,
preference_value
,
expected_status
=
400
)
response
=
self
.
send_put
(
self
.
client
,
preference_value
,
expected_status
=
400
)
self
.
assertEqual
(
self
.
assertEqual
(
response
.
data
,
response
.
data
,
...
@@ -505,7 +505,7 @@ class TestPreferencesDetailAPI(UserAPITestCase):
...
@@ -505,7 +505,7 @@ class TestPreferencesDetailAPI(UserAPITestCase):
"""
"""
Test that a client cannot create preferences with bad keys
Test that a client cannot create preferences with bad keys
"""
"""
self
.
client
.
login
(
username
=
self
.
user
.
username
,
password
=
self
.
test_password
)
self
.
client
.
login
(
username
=
self
.
user
.
username
,
password
=
TEST_PASSWORD
)
too_long_preference_key
=
"x"
*
256
too_long_preference_key
=
"x"
*
256
new_value
=
"new value"
new_value
=
"new value"
...
@@ -544,7 +544,7 @@ class TestPreferencesDetailAPI(UserAPITestCase):
...
@@ -544,7 +544,7 @@ class TestPreferencesDetailAPI(UserAPITestCase):
"""
"""
Test that a client (logged in) can update a preference.
Test that a client (logged in) can update a preference.
"""
"""
self
.
client
.
login
(
username
=
self
.
user
.
username
,
password
=
self
.
test_password
)
self
.
client
.
login
(
username
=
self
.
user
.
username
,
password
=
TEST_PASSWORD
)
self
.
send_put
(
self
.
client
,
preference_value
)
self
.
send_put
(
self
.
client
,
preference_value
)
response
=
self
.
send_get
(
self
.
client
)
response
=
self
.
send_get
(
self
.
client
)
self
.
assertEqual
(
unicode
(
preference_value
),
response
.
data
)
self
.
assertEqual
(
unicode
(
preference_value
),
response
.
data
)
...
@@ -572,7 +572,7 @@ class TestPreferencesDetailAPI(UserAPITestCase):
...
@@ -572,7 +572,7 @@ class TestPreferencesDetailAPI(UserAPITestCase):
"""
"""
Test that a client (logged in) cannot update a preference to null.
Test that a client (logged in) cannot update a preference to null.
"""
"""
self
.
client
.
login
(
username
=
self
.
user
.
username
,
password
=
self
.
test_password
)
self
.
client
.
login
(
username
=
self
.
user
.
username
,
password
=
TEST_PASSWORD
)
response
=
self
.
send_put
(
self
.
client
,
preference_value
,
expected_status
=
400
)
response
=
self
.
send_put
(
self
.
client
,
preference_value
,
expected_status
=
400
)
self
.
assertEqual
(
self
.
assertEqual
(
response
.
data
,
response
.
data
,
...
@@ -588,7 +588,7 @@ class TestPreferencesDetailAPI(UserAPITestCase):
...
@@ -588,7 +588,7 @@ class TestPreferencesDetailAPI(UserAPITestCase):
"""
"""
Test that a client (logged in) can delete her own preference.
Test that a client (logged in) can delete her own preference.
"""
"""
self
.
client
.
login
(
username
=
self
.
user
.
username
,
password
=
self
.
test_password
)
self
.
client
.
login
(
username
=
self
.
user
.
username
,
password
=
TEST_PASSWORD
)
# Verify that a preference can be deleted
# Verify that a preference can be deleted
self
.
send_delete
(
self
.
client
)
self
.
send_delete
(
self
.
client
)
...
...
openedx/core/djangoapps/user_api/urls.py
View file @
7fe8e475
...
@@ -6,7 +6,7 @@ from django.conf import settings
...
@@ -6,7 +6,7 @@ from django.conf import settings
from
django.conf.urls
import
patterns
,
url
from
django.conf.urls
import
patterns
,
url
from
..profile_images.views
import
ProfileImageView
from
..profile_images.views
import
ProfileImageView
from
.accounts.views
import
AccountViewSet
from
.accounts.views
import
Account
DeactivationView
,
Account
ViewSet
from
.preferences.views
import
PreferencesView
,
PreferencesDetailView
from
.preferences.views
import
PreferencesView
,
PreferencesDetailView
from
.verification_api.views
import
PhotoVerificationStatusView
from
.verification_api.views
import
PhotoVerificationStatusView
...
@@ -34,6 +34,11 @@ urlpatterns = patterns(
...
@@ -34,6 +34,11 @@ urlpatterns = patterns(
name
=
'accounts_profile_image_api'
name
=
'accounts_profile_image_api'
),
),
url
(
url
(
r'^v1/accounts/{}/deactivate/$'
.
format
(
settings
.
USERNAME_PATTERN
),
AccountDeactivationView
.
as_view
(),
name
=
'accounts_deactivation'
),
url
(
r'^v1/accounts/{}/verification_status/$'
.
format
(
settings
.
USERNAME_PATTERN
),
r'^v1/accounts/{}/verification_status/$'
.
format
(
settings
.
USERNAME_PATTERN
),
PhotoVerificationStatusView
.
as_view
(),
PhotoVerificationStatusView
.
as_view
(),
name
=
'verification_status'
name
=
'verification_status'
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment