Merge branch master into bees
This commit is contained in:
commit
1d8ec59362
164 changed files with 6048 additions and 1911 deletions
|
@ -7,11 +7,13 @@ from flask.ext.principal import identity_changed, AnonymousIdentity
|
|||
|
||||
from app import app, billing as stripe, authentication
|
||||
from endpoints.api import (ApiResource, nickname, resource, validate_json_request, request_error,
|
||||
log_action, internal_only, NotFound, require_user_admin, path_param,
|
||||
InvalidToken, require_scope, format_date, hide_if, show_if, license_error,
|
||||
define_json_response)
|
||||
log_action, internal_only, NotFound, require_user_admin, parse_args,
|
||||
query_param, InvalidToken, require_scope, format_date, hide_if, show_if,
|
||||
license_error, require_fresh_login, path_param, define_json_response)
|
||||
from endpoints.api.subscribe import subscribe
|
||||
from endpoints.common import common_login
|
||||
from endpoints.api.team import try_accept_invite
|
||||
|
||||
from data import model
|
||||
from data.billing import get_plan
|
||||
from auth.permissions import (AdministerOrganizationPermission, CreateRepositoryPermission,
|
||||
|
@ -19,7 +21,8 @@ from auth.permissions import (AdministerOrganizationPermission, CreateRepository
|
|||
from auth.auth_context import get_authenticated_user
|
||||
from auth import scopes
|
||||
from util.gravatar import compute_hash
|
||||
from util.useremails import (send_confirmation_email, send_recovery_email, send_change_email)
|
||||
from util.useremails import (send_confirmation_email, send_recovery_email, send_change_email, send_password_changed)
|
||||
from util.names import parse_single_urn
|
||||
|
||||
import features
|
||||
|
||||
|
@ -40,9 +43,15 @@ def user_view(user):
|
|||
organizations = model.get_user_organizations(user.username)
|
||||
|
||||
def login_view(login):
|
||||
try:
|
||||
metadata = json.loads(login.metadata_json)
|
||||
except:
|
||||
metadata = {}
|
||||
|
||||
return {
|
||||
'service': login.service.name,
|
||||
'service_identifier': login.service_ident,
|
||||
'metadata': metadata
|
||||
}
|
||||
|
||||
logins = model.list_federated_logins(user)
|
||||
|
@ -89,6 +98,7 @@ class User(ApiResource):
|
|||
""" Operations related to users. """
|
||||
schemas = {
|
||||
'NewUser': {
|
||||
|
||||
'id': 'NewUser',
|
||||
'type': 'object',
|
||||
'description': 'Fields which must be specified for a new user.',
|
||||
|
@ -185,6 +195,7 @@ class User(ApiResource):
|
|||
return user_view(user)
|
||||
|
||||
@require_user_admin
|
||||
@require_fresh_login
|
||||
@nickname('changeUserDetails')
|
||||
@internal_only
|
||||
@validate_json_request('UpdateUser')
|
||||
|
@ -194,12 +205,15 @@ class User(ApiResource):
|
|||
user = get_authenticated_user()
|
||||
user_data = request.get_json()
|
||||
|
||||
try:
|
||||
try:
|
||||
if 'password' in user_data:
|
||||
logger.debug('Changing password for user: %s', user.username)
|
||||
log_action('account_change_password', user.username)
|
||||
model.change_password(user, user_data['password'])
|
||||
|
||||
if features.MAILING:
|
||||
send_password_changed(user.username, user.email)
|
||||
|
||||
if 'invoice_email' in user_data:
|
||||
logger.debug('Changing invoice_email for user: %s', user.username)
|
||||
model.change_invoice_email(user, user_data['invoice_email'])
|
||||
|
@ -210,22 +224,30 @@ class User(ApiResource):
|
|||
# Email already used.
|
||||
raise request_error(message='E-mail address already used')
|
||||
|
||||
logger.debug('Sending email to change email address for user: %s',
|
||||
user.username)
|
||||
code = model.create_confirm_email_code(user, new_email=new_email)
|
||||
send_change_email(user.username, user_data['email'], code.code)
|
||||
if features.MAILING:
|
||||
logger.debug('Sending email to change email address for user: %s',
|
||||
user.username)
|
||||
code = model.create_confirm_email_code(user, new_email=new_email)
|
||||
send_change_email(user.username, user_data['email'], code.code)
|
||||
else:
|
||||
model.update_email(user, new_email, auto_verify=not features.MAILING)
|
||||
|
||||
except model.InvalidPasswordException, ex:
|
||||
raise request_error(exception=ex)
|
||||
|
||||
return user_view(user)
|
||||
|
||||
@show_if(features.USER_CREATION)
|
||||
@nickname('createNewUser')
|
||||
@parse_args
|
||||
@query_param('inviteCode', 'Invitation code given for creating the user.', type=str,
|
||||
default='')
|
||||
@internal_only
|
||||
@validate_json_request('NewUser')
|
||||
def post(self):
|
||||
def post(self, args):
|
||||
""" Create a new user. """
|
||||
user_data = request.get_json()
|
||||
invite_code = args['inviteCode']
|
||||
|
||||
existing_user = model.get_user(user_data['username'])
|
||||
if existing_user:
|
||||
|
@ -233,10 +255,29 @@ class User(ApiResource):
|
|||
|
||||
try:
|
||||
new_user = model.create_user(user_data['username'], user_data['password'],
|
||||
user_data['email'])
|
||||
code = model.create_confirm_email_code(new_user)
|
||||
send_confirmation_email(new_user.username, new_user.email, code.code)
|
||||
return 'Created', 201
|
||||
user_data['email'], auto_verify=not features.MAILING)
|
||||
|
||||
# Handle any invite codes.
|
||||
parsed_invite = parse_single_urn(invite_code)
|
||||
if parsed_invite is not None:
|
||||
if parsed_invite[0] == 'teaminvite':
|
||||
# Add the user to the team.
|
||||
try:
|
||||
try_accept_invite(invite_code, new_user)
|
||||
except model.DataModelException:
|
||||
pass
|
||||
|
||||
|
||||
if features.MAILING:
|
||||
code = model.create_confirm_email_code(new_user)
|
||||
send_confirmation_email(new_user.username, new_user.email, code.code)
|
||||
return {
|
||||
'awaiting_verification': True
|
||||
}
|
||||
else:
|
||||
common_login(new_user)
|
||||
return user_view(new_user)
|
||||
|
||||
except model.TooManyUsersException as ex:
|
||||
raise license_error(exception=ex)
|
||||
except model.DataModelException as ex:
|
||||
|
@ -399,6 +440,37 @@ class Signin(ApiResource):
|
|||
return conduct_signin(username, password)
|
||||
|
||||
|
||||
@resource('/v1/signin/verify')
|
||||
@internal_only
|
||||
class VerifyUser(ApiResource):
|
||||
""" Operations for verifying the existing user. """
|
||||
schemas = {
|
||||
'VerifyUser': {
|
||||
'id': 'VerifyUser',
|
||||
'type': 'object',
|
||||
'description': 'Information required to verify the signed in user.',
|
||||
'required': [
|
||||
'password',
|
||||
],
|
||||
'properties': {
|
||||
'password': {
|
||||
'type': 'string',
|
||||
'description': 'The user\'s password',
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@require_user_admin
|
||||
@nickname('verifyUser')
|
||||
@validate_json_request('VerifyUser')
|
||||
def post(self):
|
||||
""" Verifies the signed in the user with the specified credentials. """
|
||||
signin_data = request.get_json()
|
||||
password = signin_data['password']
|
||||
return conduct_signin(get_authenticated_user().username, password)
|
||||
|
||||
|
||||
@resource('/v1/signout')
|
||||
@internal_only
|
||||
class Signout(ApiResource):
|
||||
|
@ -411,7 +483,21 @@ class Signout(ApiResource):
|
|||
return {'success': True}
|
||||
|
||||
|
||||
|
||||
@resource('/v1/detachexternal/<servicename>')
|
||||
@internal_only
|
||||
class DetachExternal(ApiResource):
|
||||
""" Resource for detaching an external login. """
|
||||
@require_user_admin
|
||||
@nickname('detachExternalLogin')
|
||||
def post(self, servicename):
|
||||
""" Request that the current user be detached from the external login service. """
|
||||
model.detach_external_login(get_authenticated_user(), servicename)
|
||||
return {'success': True}
|
||||
|
||||
|
||||
@resource("/v1/recovery")
|
||||
@show_if(features.MAILING)
|
||||
@internal_only
|
||||
class Recovery(ApiResource):
|
||||
""" Resource for requesting a password recovery email. """
|
||||
|
@ -446,11 +532,24 @@ class Recovery(ApiResource):
|
|||
@internal_only
|
||||
class UserNotificationList(ApiResource):
|
||||
@require_user_admin
|
||||
@parse_args
|
||||
@query_param('page', 'Offset page number. (int)', type=int, default=0)
|
||||
@query_param('limit', 'Limit on the number of results (int)', type=int, default=5)
|
||||
@nickname('listUserNotifications')
|
||||
def get(self):
|
||||
notifications = model.list_notifications(get_authenticated_user())
|
||||
def get(self, args):
|
||||
page = args['page']
|
||||
limit = args['limit']
|
||||
|
||||
notifications = list(model.list_notifications(get_authenticated_user(), page=page, limit=limit + 1))
|
||||
has_more = False
|
||||
|
||||
if len(notifications) > limit:
|
||||
has_more = True
|
||||
notifications = notifications[0:limit]
|
||||
|
||||
return {
|
||||
'notifications': [notification_view(notification) for notification in notifications]
|
||||
'notifications': [notification_view(notification) for notification in notifications],
|
||||
'additional': has_more
|
||||
}
|
||||
|
||||
|
||||
|
|
Reference in a new issue