Merge branch 'driversed' of ssh://bitbucket.org/yackob03/quay into driversed

This commit is contained in:
Jake Moshenko 2014-05-29 11:24:44 -04:00
commit 8e6328a5f0
11 changed files with 64 additions and 13 deletions

3
.gitignore vendored
View file

@ -7,3 +7,6 @@ grunt/node_modules
dist dist
dest dest
node_modules node_modules
static/ldn
static/fonts
stack_local

View file

@ -18,7 +18,7 @@ def build_requests_session():
# values are set to the frontend, DO NOT PLACE ANY SECRETS OR KEYS in this list. # values are set to the frontend, DO NOT PLACE ANY SECRETS OR KEYS in this list.
CLIENT_WHITELIST = ['SERVER_HOSTNAME', 'PREFERRED_URL_SCHEME', 'GITHUB_CLIENT_ID', CLIENT_WHITELIST = ['SERVER_HOSTNAME', 'PREFERRED_URL_SCHEME', 'GITHUB_CLIENT_ID',
'GITHUB_LOGIN_CLIENT_ID', 'MIXPANEL_KEY', 'STRIPE_PUBLISHABLE_KEY', 'GITHUB_LOGIN_CLIENT_ID', 'MIXPANEL_KEY', 'STRIPE_PUBLISHABLE_KEY',
'ENTERPRISE_LOGO_URL', 'SENTRY_PUBLIC_DSN'] 'ENTERPRISE_LOGO_URL', 'SENTRY_PUBLIC_DSN', 'AUTHENTICATION_TYPE']
def getFrontendVisibleConfig(config_dict): def getFrontendVisibleConfig(config_dict):

View file

@ -66,6 +66,10 @@ class Unauthorized(ApiException):
ApiException.__init__(self, 'insufficient_scope', 403, 'Unauthorized', payload) ApiException.__init__(self, 'insufficient_scope', 403, 'Unauthorized', payload)
class ExceedsLicenseException(ApiException):
def __init__(self, payload=None):
ApiException.__init__(self, None, 402, 'Payment Required', payload)
class NotFound(ApiException): class NotFound(ApiException):
def __init__(self, payload=None): def __init__(self, payload=None):
@ -275,6 +279,10 @@ def request_error(exception=None, **kwargs):
raise InvalidRequest(message, data) raise InvalidRequest(message, data)
def license_error(exception=None):
raise ExceedsLicenseException()
def log_action(kind, user_or_orgname, metadata=None, repo=None): def log_action(kind, user_or_orgname, metadata=None, repo=None):
if not metadata: if not metadata:
metadata = {} metadata = {}

View file

@ -8,7 +8,7 @@ from flask.ext.principal import identity_changed, AnonymousIdentity
from app import app, billing as stripe, authentication from app import app, billing as stripe, authentication
from endpoints.api import (ApiResource, nickname, resource, validate_json_request, request_error, from endpoints.api import (ApiResource, nickname, resource, validate_json_request, request_error,
log_action, internal_only, NotFound, require_user_admin, log_action, internal_only, NotFound, require_user_admin,
InvalidToken, require_scope, format_date, hide_if, show_if) InvalidToken, require_scope, format_date, hide_if, show_if, license_error)
from endpoints.api.subscribe import subscribe from endpoints.api.subscribe import subscribe
from endpoints.common import common_login from endpoints.common import common_login
from data import model from data import model
@ -192,6 +192,8 @@ class User(ApiResource):
code = model.create_confirm_email_code(new_user) code = model.create_confirm_email_code(new_user)
send_confirmation_email(new_user.username, new_user.email, code.code) send_confirmation_email(new_user.username, new_user.email, code.code)
return 'Created', 201 return 'Created', 201
except model.TooManyUsersException as ex:
raise license_error(exception=ex)
except model.DataModelException as ex: except model.DataModelException as ex:
raise request_error(exception=ex) raise request_error(exception=ex)
@ -226,7 +228,12 @@ def conduct_signin(username_or_email, password):
needs_email_verification = False needs_email_verification = False
invalid_credentials = False invalid_credentials = False
verified = authentication.verify_user(username_or_email, password) verified = None
try:
verified = authentication.verify_user(username_or_email, password)
except model.TooManyUsersException as ex:
raise license_error(exception=ex)
if verified: if verified:
if common_login(verified): if common_login(verified):
return {'success': True} return {'success': True}
@ -244,6 +251,7 @@ def conduct_signin(username_or_email, password):
@resource('/v1/user/convert') @resource('/v1/user/convert')
@internal_only @internal_only
@show_if(app.config['AUTHENTICATION_TYPE'] == 'Database')
class ConvertToOrganization(ApiResource): class ConvertToOrganization(ApiResource):
""" Operations for converting a user to an organization. """ """ Operations for converting a user to an organization. """
schemas = { schemas = {

View file

@ -164,6 +164,7 @@ def render_page_template(name, **kwargs):
is_debug=str(app.config.get('DEBUGGING', False)).lower(), is_debug=str(app.config.get('DEBUGGING', False)).lower(),
show_chat=features.OLARK_CHAT, show_chat=features.OLARK_CHAT,
cache_buster=cache_buster, cache_buster=cache_buster,
has_billing=features.BILLING,
**kwargs)) **kwargs))
resp.headers['X-FRAME-OPTIONS'] = 'DENY' resp.headers['X-FRAME-OPTIONS'] = 'DENY'

View file

@ -112,7 +112,12 @@ def create_user():
else: else:
# New user case # New user case
profile.debug('Creating user') profile.debug('Creating user')
new_user = model.create_user(username, password, user_data['email']) new_user = None
try:
new_user = model.create_user(username, password, user_data['email'])
except model.TooManyUsersException as ex:
abort(402, 'Seat limit has been reached for this license', issue='seat-limit')
profile.debug('Creating email code for user') profile.debug('Creating email code for user')
code = model.create_confirm_email_code(new_user) code = model.create_confirm_email_code(new_user)

View file

@ -4755,8 +4755,8 @@ quayApp.directive('ngVisible', function () {
}; };
}); });
quayApp.run(['$location', '$rootScope', 'Restangular', 'UserService', 'PlanService', '$http', '$timeout', 'CookieService', quayApp.run(['$location', '$rootScope', 'Restangular', 'UserService', 'PlanService', '$http', '$timeout', 'CookieService', 'Features',
function($location, $rootScope, Restangular, UserService, PlanService, $http, $timeout, CookieService) { function($location, $rootScope, Restangular, UserService, PlanService, $http, $timeout, CookieService, Features) {
// Handle session security. // Handle session security.
Restangular.setDefaultRequestParams(['post', 'put', 'remove', 'delete'], {'_csrf_token': window.__token || ''}); Restangular.setDefaultRequestParams(['post', 'put', 'remove', 'delete'], {'_csrf_token': window.__token || ''});
@ -4770,6 +4770,11 @@ quayApp.run(['$location', '$rootScope', 'Restangular', 'UserService', 'PlanServi
} }
} }
if (!Features.BILLING && response.status == 402) {
$('#overlicenseModal').modal({});
return false;
}
if (response.status == 500) { if (response.status == 500) {
document.location = '/500'; document.location = '/500';
return false; return false;

View file

@ -1589,7 +1589,7 @@ function RepoAdminCtrl($scope, Restangular, ApiService, KeyService, $routeParams
} }
function UserAdminCtrl($scope, $timeout, $location, ApiService, PlanService, UserService, CookieService, KeyService, function UserAdminCtrl($scope, $timeout, $location, ApiService, PlanService, UserService, CookieService, KeyService,
$routeParams, $http, UIService, Features) { $routeParams, $http, UIService, Features, Config) {
$scope.Features = Features; $scope.Features = Features;
if ($routeParams['migrate']) { if ($routeParams['migrate']) {
@ -1597,11 +1597,9 @@ function UserAdminCtrl($scope, $timeout, $location, ApiService, PlanService, Use
} }
UserService.updateUserIn($scope, function(user) { UserService.updateUserIn($scope, function(user) {
if (!Features.GITHUB_LOGIN) { return; }
$scope.cuser = jQuery.extend({}, user); $scope.cuser = jQuery.extend({}, user);
if ($scope.cuser.logins) { if (Features.GITHUB_LOGIN && $scope.cuser.logins) {
for (var i = 0; i < $scope.cuser.logins.length; i++) { for (var i = 0; i < $scope.cuser.logins.length; i++) {
if ($scope.cuser.logins[i].service == 'github') { if ($scope.cuser.logins[i].service == 'github') {
var githubId = $scope.cuser.logins[i].service_identifier; var githubId = $scope.cuser.logins[i].service_identifier;
@ -1694,6 +1692,8 @@ function UserAdminCtrl($scope, $timeout, $location, ApiService, PlanService, Use
}; };
$scope.reallyConvert = function() { $scope.reallyConvert = function() {
if (Config.AUTHENTICATION_TYPE != 'Database') { return; }
$scope.loading = true; $scope.loading = true;
var data = { var data = {

View file

@ -10,7 +10,7 @@
Create New Organization Create New Organization
</button> </button>
</a> </a>
<a href="/user/?migrate" ng-show="!user.anonymous" data-title="Starts the process to convert this account into an organization" bs-tooltip="tooltip.title"> <a href="/user/?migrate" data-title="Starts the process to convert this account into an organization" bs-tooltip="tooltip.title" quay-show="Config.AUTHENTICATION_TYPE == 'Database' && !user.anonymous">
<button class="btn btn-primary"> <button class="btn btn-primary">
<i class="fa fa-caret-square-o-right"></i> <i class="fa fa-caret-square-o-right"></i>
Convert account Convert account

View file

@ -38,7 +38,9 @@
<li quay-show="Features.USER_LOG_ACCESS || hasPaidBusinessPlan"> <li quay-show="Features.USER_LOG_ACCESS || hasPaidBusinessPlan">
<a href="javascript:void(0)" data-toggle="tab" data-target="#logs" ng-click="loadLogs()">Usage Logs</a> <a href="javascript:void(0)" data-toggle="tab" data-target="#logs" ng-click="loadLogs()">Usage Logs</a>
</li> </li>
<li><a href="javascript:void(0)" data-toggle="tab" data-target="#migrate" id="migrateTab">Convert to Organization</a></li> <li quay-show="Config.AUTHENTICATION_TYPE == 'Database'">
<a href="javascript:void(0)" data-toggle="tab" data-target="#migrate" id="migrateTab">Convert to Organization</a>
</li>
</ul> </ul>
</div> </div>
@ -197,7 +199,7 @@
</div> </div>
<!-- Convert to organization tab --> <!-- Convert to organization tab -->
<div id="migrate" class="tab-pane"> <div id="migrate" class="tab-pane" quay-show="Config.AUTHENTICATION_TYPE == 'Database'">
<!-- Step 0 --> <!-- Step 0 -->
<div class="panel" ng-show="convertStep == 0"> <div class="panel" ng-show="convertStep == 0">
<div class="panel-body" ng-show="user.organizations.length > 0"> <div class="panel-body" ng-show="user.organizations.length > 0">

View file

@ -35,4 +35,23 @@
</div><!-- /.modal-dialog --> </div><!-- /.modal-dialog -->
</div><!-- /.modal --> </div><!-- /.modal -->
{% if not has_billing %}
<!-- Modal message dialog -->
<div class="modal fade" id="overlicenseModal" data-backdrop="static">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Cannot create user</h4>
</div>
<div class="modal-body">
A new user cannot be created as this organization has reached its licensed seat count. Please contact your administrator.
</div>
<div class="modal-footer">
<a href="javascript:void(0)" class="btn btn-primary" data-dismiss="modal" onclick="location = '/signin'">Sign In</a>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
{% endif %}
{% endblock %} {% endblock %}