Merge "Removed Nickname"
This commit is contained in:
commit
4ef8732f8f
@ -62,7 +62,6 @@ class ErrorMessages(object):
|
|||||||
|
|
||||||
INVALID_NO_EMAIL = _('Please permit access to your email address.')
|
INVALID_NO_EMAIL = _('Please permit access to your email address.')
|
||||||
INVALID_NO_NAME = _('Please permit access to your name.')
|
INVALID_NO_NAME = _('Please permit access to your name.')
|
||||||
INVALID_NO_NICKNAME = _('Please permit access to your nickname.')
|
|
||||||
|
|
||||||
INVALID_TOKEN_GRANT_TYPE = _('Only grant types "authorization_code" and '
|
INVALID_TOKEN_GRANT_TYPE = _('Only grant types "authorization_code" and '
|
||||||
'"refresh_token" are supported.')
|
'"refresh_token" are supported.')
|
||||||
|
@ -112,12 +112,10 @@ class SkeletonValidator(RequestValidator):
|
|||||||
openid = request._params["openid.claimed_id"]
|
openid = request._params["openid.claimed_id"]
|
||||||
email = request._params["openid.sreg.email"]
|
email = request._params["openid.sreg.email"]
|
||||||
full_name = request._params["openid.sreg.fullname"]
|
full_name = request._params["openid.sreg.fullname"]
|
||||||
username = request._params["openid.sreg.nickname"]
|
|
||||||
last_login = datetime.datetime.now(pytz.utc)
|
last_login = datetime.datetime.now(pytz.utc)
|
||||||
|
|
||||||
user = user_api.user_get_by_openid(openid)
|
user = user_api.user_get_by_openid(openid)
|
||||||
user_dict = {"full_name": full_name,
|
user_dict = {"full_name": full_name,
|
||||||
"username": username,
|
|
||||||
"email": email,
|
"email": email,
|
||||||
"last_login": last_login}
|
"last_login": last_login}
|
||||||
|
|
||||||
|
@ -108,7 +108,7 @@ class OpenIdClient(object):
|
|||||||
"openid.return_to": return_to_url,
|
"openid.return_to": return_to_url,
|
||||||
|
|
||||||
"openid.ns.sreg": "http://openid.net/sreg/1.0",
|
"openid.ns.sreg": "http://openid.net/sreg/1.0",
|
||||||
"openid.sreg.required": "fullname,email,nickname",
|
"openid.sreg.required": "fullname,email",
|
||||||
|
|
||||||
"openid.ns.ext2": "http://openid.net/srv/ax/1.0",
|
"openid.ns.ext2": "http://openid.net/srv/ax/1.0",
|
||||||
"openid.ext2.mode": "fetch_request",
|
"openid.ext2.mode": "fetch_request",
|
||||||
@ -146,7 +146,6 @@ class OpenIdClient(object):
|
|||||||
required_parameters = {
|
required_parameters = {
|
||||||
'openid.sreg.email': e_msg.INVALID_NO_EMAIL,
|
'openid.sreg.email': e_msg.INVALID_NO_EMAIL,
|
||||||
'openid.sreg.fullname': e_msg.INVALID_NO_NAME,
|
'openid.sreg.fullname': e_msg.INVALID_NO_NAME,
|
||||||
'openid.sreg.nickname': e_msg.INVALID_NO_NICKNAME,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for name, error in six.iteritems(required_parameters):
|
for name, error in six.iteritems(required_parameters):
|
||||||
|
@ -41,7 +41,7 @@ class SearchEngine(object):
|
|||||||
models.Story: ["title", "description"],
|
models.Story: ["title", "description"],
|
||||||
models.Task: ["title"],
|
models.Task: ["title"],
|
||||||
models.Comment: ["content"],
|
models.Comment: ["content"],
|
||||||
models.User: ['username', 'full_name', 'email']
|
models.User: ['full_name', 'email']
|
||||||
}
|
}
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
|
@ -59,7 +59,7 @@ class UsersController(rest.RestController):
|
|||||||
@secure(checks.guest)
|
@secure(checks.guest)
|
||||||
@wsme_pecan.wsexpose([wmodels.User], int, int, wtypes.text, wtypes.text,
|
@wsme_pecan.wsexpose([wmodels.User], int, int, wtypes.text, wtypes.text,
|
||||||
wtypes.text, wtypes.text)
|
wtypes.text, wtypes.text)
|
||||||
def get(self, marker=None, limit=None, username=None, full_name=None,
|
def get(self, marker=None, limit=None, full_name=None,
|
||||||
sort_field='id', sort_dir='asc'):
|
sort_field='id', sort_dir='asc'):
|
||||||
"""Page and filter the users in storyboard.
|
"""Page and filter the users in storyboard.
|
||||||
|
|
||||||
@ -80,12 +80,11 @@ class UsersController(rest.RestController):
|
|||||||
marker_user = users_api.user_get(marker)
|
marker_user = users_api.user_get(marker)
|
||||||
|
|
||||||
users = users_api.user_get_all(marker=marker_user, limit=limit,
|
users = users_api.user_get_all(marker=marker_user, limit=limit,
|
||||||
username=username, full_name=full_name,
|
full_name=full_name,
|
||||||
filter_non_public=True,
|
filter_non_public=True,
|
||||||
sort_field=sort_field,
|
sort_field=sort_field,
|
||||||
sort_dir=sort_dir)
|
sort_dir=sort_dir)
|
||||||
user_count = users_api.user_get_count(username=username,
|
user_count = users_api.user_get_count(full_name=full_name)
|
||||||
full_name=full_name)
|
|
||||||
|
|
||||||
# Apply the query response headers.
|
# Apply the query response headers.
|
||||||
response.headers['X-Limit'] = str(limit)
|
response.headers['X-Limit'] = str(limit)
|
||||||
|
@ -22,11 +22,6 @@ USERS_PUT_SCHEMA = {
|
|||||||
"name": "user_schema",
|
"name": "user_schema",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"username": {
|
|
||||||
"type": "string",
|
|
||||||
"minLength": CommonLength.lower_middle_length,
|
|
||||||
"maxLength": CommonLength.name_length
|
|
||||||
},
|
|
||||||
"full_name": {
|
"full_name": {
|
||||||
"type": ["string"],
|
"type": ["string"],
|
||||||
"minLength": CommonLength.lower_middle_length,
|
"minLength": CommonLength.lower_middle_length,
|
||||||
@ -45,7 +40,7 @@ USERS_PUT_SCHEMA = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
USERS_POST_SCHEMA = copy.deepcopy(USERS_PUT_SCHEMA)
|
USERS_POST_SCHEMA = copy.deepcopy(USERS_PUT_SCHEMA)
|
||||||
USERS_POST_SCHEMA["required"] = ["username", "full_name", "email"]
|
USERS_POST_SCHEMA["required"] = ["full_name", "email"]
|
||||||
|
|
||||||
USER_PREFERENCES_POST_SCHEMA = {
|
USER_PREFERENCES_POST_SCHEMA = {
|
||||||
"name": "userPreference_schema",
|
"name": "userPreference_schema",
|
||||||
|
@ -360,10 +360,6 @@ class TimeLineEvent(base.APIBase):
|
|||||||
class User(base.APIBase):
|
class User(base.APIBase):
|
||||||
"""Represents a user."""
|
"""Represents a user."""
|
||||||
|
|
||||||
username = wtypes.text
|
|
||||||
"""A short unique name, beginning with a lower-case letter or number, and
|
|
||||||
containing only letters, numbers, dots, hyphens, or plus signs"""
|
|
||||||
|
|
||||||
full_name = wtypes.text
|
full_name = wtypes.text
|
||||||
"""Full (Display) name."""
|
"""Full (Display) name."""
|
||||||
|
|
||||||
@ -385,7 +381,6 @@ class User(base.APIBase):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def sample(cls):
|
def sample(cls):
|
||||||
return cls(
|
return cls(
|
||||||
username="elbarto",
|
|
||||||
full_name="Bart Simpson",
|
full_name="Bart Simpson",
|
||||||
openid="https://login.launchpad.net/+id/Abacaba",
|
openid="https://login.launchpad.net/+id/Abacaba",
|
||||||
email="skinnerstinks@springfield.net",
|
email="skinnerstinks@springfield.net",
|
||||||
|
@ -1,22 +0,0 @@
|
|||||||
# Copyright (c) 2014 Mirantis Inc.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
||||||
# implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
|
|
||||||
from storyboard.db.api import users as users_api
|
|
||||||
|
|
||||||
|
|
||||||
def username_by_id(user_id):
|
|
||||||
user = users_api.user_get(user_id)
|
|
||||||
username = user.full_name or user.username
|
|
||||||
return username
|
|
@ -0,0 +1,63 @@
|
|||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
#
|
||||||
|
|
||||||
|
"""This migration removes the 'nickname' column from the user table.
|
||||||
|
|
||||||
|
Revision ID: 042
|
||||||
|
Revises: 041
|
||||||
|
Create Date: 2015-02-17 12:00:00
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
|
||||||
|
revision = '042'
|
||||||
|
down_revision = '041'
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
from oslo_log import log
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
LOG = log.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade(active_plugins=None, options=None):
|
||||||
|
op.drop_column('users', 'username')
|
||||||
|
|
||||||
|
# Handle the FT Index on the user table.
|
||||||
|
version_info = op.get_bind().engine.dialect.server_version_info
|
||||||
|
if version_info[0] < 5 or version_info[0] == 5 and version_info[1] < 6:
|
||||||
|
LOG.warn("MySQL version is lower than 5.6. Skipping full-text indexes")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Index for users
|
||||||
|
op.drop_index("users_fti", table_name='users')
|
||||||
|
op.execute("ALTER TABLE users "
|
||||||
|
"ADD FULLTEXT users_fti (full_name, email)")
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade(active_plugins=None, options=None):
|
||||||
|
op.add_column(
|
||||||
|
'users',
|
||||||
|
sa.Column('username', sa.Unicode(length=30), nullable=True),
|
||||||
|
)
|
||||||
|
|
||||||
|
version_info = op.get_bind().engine.dialect.server_version_info
|
||||||
|
if version_info[0] < 5 or version_info[0] == 5 and version_info[1] < 6:
|
||||||
|
LOG.warn("MySQL version is lower than 5.6. Skipping full-text indexes")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Index for users
|
||||||
|
op.drop_index("users_fti", table_name='users')
|
||||||
|
op.execute("ALTER TABLE users "
|
||||||
|
"ADD FULLTEXT users_fti (username, full_name, email)")
|
@ -140,9 +140,8 @@ class User(FullText, ModelBuilder, Base):
|
|||||||
schema.UniqueConstraint('email', name='uniq_user_email'),
|
schema.UniqueConstraint('email', name='uniq_user_email'),
|
||||||
)
|
)
|
||||||
|
|
||||||
__fulltext_columns__ = ['username', 'full_name', 'email']
|
__fulltext_columns__ = ['full_name', 'email']
|
||||||
|
|
||||||
username = Column(Unicode(CommonLength.name_length))
|
|
||||||
full_name = Column(Unicode(CommonLength.top_large_length), nullable=True)
|
full_name = Column(Unicode(CommonLength.top_large_length), nullable=True)
|
||||||
email = Column(String(CommonLength.top_large_length))
|
email = Column(String(CommonLength.top_large_length))
|
||||||
openid = Column(String(CommonLength.top_large_length))
|
openid = Column(String(CommonLength.top_large_length))
|
||||||
@ -156,7 +155,7 @@ class User(FullText, ModelBuilder, Base):
|
|||||||
|
|
||||||
preferences = relationship("UserPreference")
|
preferences = relationship("UserPreference")
|
||||||
|
|
||||||
_public_fields = ["id", "openid", "full_name", "username", "last_login",
|
_public_fields = ["id", "openid", "full_name", "last_login",
|
||||||
"enable_login"]
|
"enable_login"]
|
||||||
|
|
||||||
|
|
||||||
|
@ -97,7 +97,6 @@ class LaunchpadWriter(object):
|
|||||||
if lp_user is None:
|
if lp_user is None:
|
||||||
return lp_user
|
return lp_user
|
||||||
|
|
||||||
username = lp_user.name
|
|
||||||
display_name = lp_user.display_name
|
display_name = lp_user.display_name
|
||||||
user_link = lp_user.web_link
|
user_link = lp_user.web_link
|
||||||
|
|
||||||
@ -114,9 +113,10 @@ class LaunchpadWriter(object):
|
|||||||
except DiscoveryFailure:
|
except DiscoveryFailure:
|
||||||
# If we encounter a launchpad maintenance user,
|
# If we encounter a launchpad maintenance user,
|
||||||
# give it an invalid openid.
|
# give it an invalid openid.
|
||||||
print "WARNING: Invalid OpenID for user \'%s\'" % (username,)
|
print "WARNING: Invalid OpenID for user \'%s\'" \
|
||||||
|
% (display_name,)
|
||||||
self._openid_map[user_link] = \
|
self._openid_map[user_link] = \
|
||||||
'http://example.com/invalid/~%s' % (username,)
|
'http://example.com/invalid/~%s' % (display_name,)
|
||||||
|
|
||||||
openid = self._openid_map[user_link]
|
openid = self._openid_map[user_link]
|
||||||
|
|
||||||
@ -131,10 +131,9 @@ class LaunchpadWriter(object):
|
|||||||
# Use a temporary email address, since LP won't give this to
|
# Use a temporary email address, since LP won't give this to
|
||||||
# us and it'll be updated on first login anyway.
|
# us and it'll be updated on first login anyway.
|
||||||
user = users_api.user_create({
|
user = users_api.user_create({
|
||||||
'username': username,
|
|
||||||
'openid': openid,
|
'openid': openid,
|
||||||
'full_name': display_name,
|
'full_name': display_name,
|
||||||
'email': "%s@example.com" % (username)
|
'email': "%s@example.com" % (display_name)
|
||||||
})
|
})
|
||||||
|
|
||||||
self._user_map[openid] = user
|
self._user_map[openid] = user
|
||||||
|
@ -121,7 +121,6 @@ class TestOAuthAuthorize(BaseOAuthTest):
|
|||||||
# Check OAuth Registration parameters
|
# Check OAuth Registration parameters
|
||||||
self.assertIn('fullname', parameters['openid.sreg.required'][0])
|
self.assertIn('fullname', parameters['openid.sreg.required'][0])
|
||||||
self.assertIn('email', parameters['openid.sreg.required'][0])
|
self.assertIn('email', parameters['openid.sreg.required'][0])
|
||||||
self.assertIn('nickname', parameters['openid.sreg.required'][0])
|
|
||||||
|
|
||||||
# Check redirect URL
|
# Check redirect URL
|
||||||
redirect = parameters['openid.return_to'][0]
|
redirect = parameters['openid.return_to'][0]
|
||||||
@ -348,10 +347,9 @@ class TestOAuthAuthorizeReturn(BaseOAuthTest):
|
|||||||
"ax.type.FirstName,ax.type.LastName,claimed_id,"
|
"ax.type.FirstName,ax.type.LastName,claimed_id,"
|
||||||
"identity,mode,ns,ns.ax,ns.sreg,op_endpoint,"
|
"identity,mode,ns,ns.ax,ns.sreg,op_endpoint,"
|
||||||
"response_nonce,return_to,signed,sreg.email,"
|
"response_nonce,return_to,signed,sreg.email,"
|
||||||
"sreg.fullname,sreg.nickname",
|
"sreg.fullname",
|
||||||
"openid.sreg.email": "test@example.com",
|
"openid.sreg.email": "test@example.com",
|
||||||
"openid.sreg.fullname": "Test User",
|
"openid.sreg.fullname": "Test User",
|
||||||
"openid.sreg.nickname": "superuser"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def _mock_response(self, mock_post, valid=True):
|
def _mock_response(self, mock_post, valid=True):
|
||||||
@ -473,34 +471,6 @@ class TestOAuthAuthorizeReturn(BaseOAuthTest):
|
|||||||
error='invalid_request',
|
error='invalid_request',
|
||||||
error_description=e_msg.INVALID_NO_EMAIL)
|
error_description=e_msg.INVALID_NO_EMAIL)
|
||||||
|
|
||||||
def test_invalid_redirect_no_username(self, mock_post):
|
|
||||||
"""If the oauth response to storyboard is valid, but does not include a
|
|
||||||
first name, it should error.
|
|
||||||
|
|
||||||
TODO: Remove during work for
|
|
||||||
https://storyboard.openstack.org/#!/story/2000152
|
|
||||||
"""
|
|
||||||
self._mock_response(mock_post, valid=True)
|
|
||||||
|
|
||||||
random_state = six.text_type(uuid.uuid4())
|
|
||||||
|
|
||||||
invalid_params = self.valid_params.copy()
|
|
||||||
del invalid_params['openid.sreg.nickname']
|
|
||||||
|
|
||||||
# Simple GET with various parameters
|
|
||||||
response = self.get_json(path='/openid/authorize_return',
|
|
||||||
expect_errors=True,
|
|
||||||
state=random_state,
|
|
||||||
**invalid_params)
|
|
||||||
|
|
||||||
# Validate the redirect response
|
|
||||||
self.assertValidRedirect(response=response,
|
|
||||||
expected_status_code=302,
|
|
||||||
redirect_uri=
|
|
||||||
self.valid_params['sb_redirect_uri'],
|
|
||||||
error='invalid_request',
|
|
||||||
error_description=e_msg.INVALID_NO_NICKNAME)
|
|
||||||
|
|
||||||
|
|
||||||
class TestOAuthAccessToken(BaseOAuthTest):
|
class TestOAuthAccessToken(BaseOAuthTest):
|
||||||
"""Functional test for the /oauth/token endpoint for the generation of
|
"""Functional test for the /oauth/token endpoint for the generation of
|
||||||
|
@ -47,14 +47,12 @@ class TestDBExceptions(base.FunctionalTest):
|
|||||||
# send user first time
|
# send user first time
|
||||||
resource = '/users'
|
resource = '/users'
|
||||||
user = {
|
user = {
|
||||||
'username': 'test_duplicate',
|
|
||||||
'full_name': 'Test duplicate',
|
'full_name': 'Test duplicate',
|
||||||
'email': 'dupe@example.com'
|
'email': 'dupe@example.com'
|
||||||
}
|
}
|
||||||
|
|
||||||
response = self.post_json(resource, user)
|
response = self.post_json(resource, user)
|
||||||
users_body = response.json
|
users_body = response.json
|
||||||
self.assertEqual(user['username'], users_body['username'])
|
|
||||||
self.assertEqual(user['full_name'], users_body['full_name'])
|
self.assertEqual(user['full_name'], users_body['full_name'])
|
||||||
self.assertEqual(user['email'], users_body['email'])
|
self.assertEqual(user['email'], users_body['email'])
|
||||||
|
|
||||||
|
@ -65,17 +65,15 @@ class TestUsers(base.FunctionalTest):
|
|||||||
self.default_headers['Authorization'] = 'Bearer valid_superuser_token'
|
self.default_headers['Authorization'] = 'Bearer valid_superuser_token'
|
||||||
|
|
||||||
self.user_01 = {
|
self.user_01 = {
|
||||||
'username': 'jsonschema_test_user1',
|
|
||||||
'full_name': 'jsonschema_test_user1',
|
'full_name': 'jsonschema_test_user1',
|
||||||
'email': 'jsonschema_test_user1@test.ru',
|
'email': 'jsonschema_test_user1@test.ru',
|
||||||
'openid': 'qwerty'
|
'openid': 'qwerty'
|
||||||
}
|
}
|
||||||
|
|
||||||
self.user_02 = {
|
self.user_02 = {
|
||||||
'username': 't2',
|
'full_name': LONG_STRING,
|
||||||
'full_name': 'jsonschema_test_user2',
|
'email': 'jsonschema_test_user3@test.ru',
|
||||||
'email': 'jsonschema_test_user2@test.ru',
|
'openid': 'qwertyui'
|
||||||
'openid': 'qwertyu'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.user_03 = {
|
self.user_03 = {
|
||||||
@ -85,12 +83,6 @@ class TestUsers(base.FunctionalTest):
|
|||||||
'openid': 'qwertyui'
|
'openid': 'qwertyui'
|
||||||
}
|
}
|
||||||
|
|
||||||
self.user_04 = {
|
|
||||||
'full_name': 'jsonschema_test_user4',
|
|
||||||
'email': 'jsonschema_test_user4@test.ru',
|
|
||||||
'openid': 'qwertyuio'
|
|
||||||
}
|
|
||||||
|
|
||||||
self.put_user_01 = {
|
self.put_user_01 = {
|
||||||
'full_name': 'new full_name of regular User'
|
'full_name': 'new full_name of regular User'
|
||||||
}
|
}
|
||||||
@ -107,9 +99,7 @@ class TestUsers(base.FunctionalTest):
|
|||||||
create(self, self.user_01, self.resource)
|
create(self, self.user_01, self.resource)
|
||||||
|
|
||||||
def test_create_invalid(self):
|
def test_create_invalid(self):
|
||||||
create_invalid_length(self, self.user_02, self.resource, 'username')
|
create_invalid_length(self, self.user_02, self.resource, 'full_name')
|
||||||
create_invalid_length(self, self.user_03, self.resource, 'full_name')
|
|
||||||
create_invalid_required(self, self.user_04, self.resource, 'username')
|
|
||||||
|
|
||||||
def test_update(self):
|
def test_update(self):
|
||||||
resource = "".join([self.resource, "/2"])
|
resource = "".join([self.resource, "/2"])
|
||||||
|
@ -87,5 +87,5 @@ class TestSearchUsers(base.FunctionalTest):
|
|||||||
self.default_headers['Authorization'] = 'Bearer valid_user_token'
|
self.default_headers['Authorization'] = 'Bearer valid_user_token'
|
||||||
|
|
||||||
def testBrowse(self):
|
def testBrowse(self):
|
||||||
result = self.get_json(self.resource + '?username=regularuser')
|
result = self.get_json(self.resource + '?full_name=Regular')
|
||||||
self.assertEqual(1, len(result))
|
self.assertEqual(1, len(result))
|
||||||
|
@ -40,19 +40,16 @@ def load():
|
|||||||
# Load users
|
# Load users
|
||||||
load_data([
|
load_data([
|
||||||
User(id=1,
|
User(id=1,
|
||||||
username='superuser',
|
|
||||||
email='superuser@example.com',
|
email='superuser@example.com',
|
||||||
openid='superuser_openid',
|
openid='superuser_openid',
|
||||||
full_name='Super User',
|
full_name='Super User',
|
||||||
is_superuser=True),
|
is_superuser=True),
|
||||||
User(id=2,
|
User(id=2,
|
||||||
username='regularuser',
|
|
||||||
email='regularuser@example.com',
|
email='regularuser@example.com',
|
||||||
openid='regularuser_openid',
|
openid='regularuser_openid',
|
||||||
full_name='Regular User',
|
full_name='Regular User',
|
||||||
is_superuser=False),
|
is_superuser=False),
|
||||||
User(id=3,
|
User(id=3,
|
||||||
username='otheruser',
|
|
||||||
email='otheruser@example.com',
|
email='otheruser@example.com',
|
||||||
openid='otheruser_openid',
|
openid='otheruser_openid',
|
||||||
full_name='Other User',
|
full_name='Other User',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user