Merge branch 'master' into touchdown

Conflicts:
	static/js/app.js
	static/partials/organizations.html
	test/data/test.db
This commit is contained in:
Joseph Schorr 2014-06-03 13:22:26 -04:00
commit c630d7e948
65 changed files with 1843 additions and 273 deletions

View file

@ -66,6 +66,10 @@ class Unauthorized(ApiException):
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):
def __init__(self, payload=None):
@ -275,6 +279,10 @@ def request_error(exception=None, **kwargs):
raise InvalidRequest(message, data)
def license_error(exception=None):
raise ExceedsLicenseException()
def log_action(kind, user_or_orgname, metadata=None, repo=None):
if not metadata:
metadata = {}

View file

@ -36,7 +36,9 @@ def get_card(user):
card_info = {
'owner': default_card.name,
'type': default_card.type,
'last4': default_card.last4
'last4': default_card.last4,
'exp_month': default_card.exp_month,
'exp_year': default_card.exp_year
}
return {'card': card_info}

View file

@ -54,7 +54,7 @@ class SeatUsage(ApiResource):
if SuperUserPermission().can():
return {
'count': model.get_active_user_count(),
'allowed': app.config.get('LICENSE_SEAT_COUNT', 0)
'allowed': app.config.get('LICENSE_USER_LIMIT', 0)
}
abort(403)

View file

@ -5,10 +5,10 @@ from flask import request
from flask.ext.login import logout_user
from flask.ext.principal import identity_changed, AnonymousIdentity
from app import app, billing as stripe
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,
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.common import common_login
from data import model
@ -18,8 +18,7 @@ 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.email import (send_confirmation_email, send_recovery_email,
send_change_email)
from util.useremails import (send_confirmation_email, send_recovery_email, send_change_email)
import features
@ -193,6 +192,8 @@ class User(ApiResource):
code = model.create_confirm_email_code(new_user)
send_confirmation_email(new_user.username, new_user.email, code.code)
return 'Created', 201
except model.TooManyUsersException as ex:
raise license_error(exception=ex)
except model.DataModelException as ex:
raise request_error(exception=ex)
@ -227,7 +228,12 @@ def conduct_signin(username_or_email, password):
needs_email_verification = False
invalid_credentials = False
verified = model.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 common_login(verified):
return {'success': True}
@ -245,6 +251,7 @@ def conduct_signin(username_or_email, password):
@resource('/v1/user/convert')
@internal_only
@show_if(app.config['AUTHENTICATION_TYPE'] == 'Database')
class ConvertToOrganization(ApiResource):
""" Operations for converting a user to an organization. """
schemas = {
@ -289,7 +296,7 @@ class ConvertToOrganization(ApiResource):
# Ensure that the sign in credentials work.
admin_password = convert_data['adminPassword']
if not model.verify_user(admin_username, admin_password):
if not authentication.verify_user(admin_username, admin_password):
raise request_error(reason='invaliduser',
message='The admin user credentials are not valid')

View file

@ -35,7 +35,11 @@ def exchange_github_code_for_token(code, for_login=True):
get_access_token = client.post(app.config['GITHUB_TOKEN_URL'],
params=payload, headers=headers)
token = get_access_token.json()['access_token']
json_data = get_access_token.json()
if not json_data:
return ''
token = json_data.get('access_token', '')
return token
@ -83,7 +87,7 @@ def github_oauth_callback():
# try to create the user
try:
to_login = model.create_federated_user(username, found_email, 'github',
github_id)
github_id, set_password_notification=True)
# Success, tell analytics
analytics.track(to_login.username, 'register', {'service': 'github'})
@ -115,6 +119,7 @@ def github_oauth_attach():
@callback.route('/github/callback/trigger/<path:repository>', methods=['GET'])
@route_show_if(features.GITHUB_BUILD)
@require_session_login
@parse_repository_name
def attach_github_build_trigger(namespace, repository):

View file

@ -9,14 +9,14 @@ from flask.ext.principal import identity_changed
from random import SystemRandom
from data import model
from data.queue import dockerfile_build_queue
from app import app, login_manager
from app import app, login_manager, dockerfile_build_queue
from auth.permissions import QuayDeferredPermissionUser
from auth import scopes
from endpoints.api.discovery import swagger_route_data
from werkzeug.routing import BaseConverter
from functools import wraps
from config import getFrontendVisibleConfig
from external_libraries import get_external_javascript, get_external_css
import features
@ -147,7 +147,12 @@ def render_page_template(name, **kwargs):
main_scripts = ['dist/quay-frontend.min.js']
cache_buster = random_string()
external_styles = get_external_css(local=not app.config.get('USE_CDN', True))
external_scripts = get_external_javascript(local=not app.config.get('USE_CDN', True))
resp = make_response(render_template(name, route_data=json.dumps(get_route_data()),
external_styles=external_styles,
external_scripts=external_scripts,
main_styles=main_styles,
library_styles=library_styles,
main_scripts=main_scripts,
@ -159,6 +164,7 @@ def render_page_template(name, **kwargs):
is_debug=str(app.config.get('DEBUGGING', False)).lower(),
show_chat=features.OLARK_CHAT,
cache_buster=cache_buster,
has_billing=features.BILLING,
**kwargs))
resp.headers['X-FRAME-OPTIONS'] = 'DENY'

View file

@ -8,12 +8,11 @@ from collections import OrderedDict
from data import model
from data.model import oauth
from data.queue import webhook_queue
from app import analytics, app
from app import analytics, app, webhook_queue, authentication, userevents
from auth.auth import process_auth
from auth.auth_context import get_authenticated_user, get_validated_token, get_validated_oauth_token
from util.names import parse_repository_name
from util.email import send_confirmation_email
from util.useremails import send_confirmation_email
from auth.permissions import (ModifyRepositoryPermission, UserAdminPermission,
ReadRepositoryPermission, CreateRepositoryPermission)
@ -95,18 +94,17 @@ def create_user():
abort(400, 'Invalid robot account or password.',
issue='robot-login-failure')
existing_user = model.get_user(username)
if existing_user:
verified = model.verify_user(username, password)
if authentication.user_exists(username):
verified = authentication.verify_user(username, password)
if verified:
# Mark that the user was logged in.
event = app.config['USER_EVENTS'].get_event(username)
event = userevents.get_event(username)
event.publish_event_data('docker-cli', {'action': 'login'})
return success
else:
# Mark that the login failed.
event = app.config['USER_EVENTS'].get_event(username)
event = userevents.get_event(username)
event.publish_event_data('docker-cli', {'action': 'loginfailure'})
abort(400, 'Invalid password.', issue='login-failure')
@ -114,7 +112,12 @@ def create_user():
else:
# New user case
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')
code = model.create_confirm_email_code(new_user)
@ -260,7 +263,7 @@ def create_repository(namespace, repository):
'namespace': namespace
}
event = app.config['USER_EVENTS'].get_event(username)
event = userevents.get_event(username)
event.publish_event_data('docker-cli', user_data)
elif get_validated_token():
@ -308,7 +311,7 @@ def update_images(namespace, repository):
'namespace': namespace
}
event = app.config['USER_EVENTS'].get_event(username)
event = userevents.get_event(username)
event.publish_event_data('docker-cli', user_data)
profile.debug('GCing repository')

View file

@ -3,8 +3,8 @@ import json
from flask import request, Blueprint, abort, Response
from flask.ext.login import current_user
from data import userevent
from auth.auth import require_session_login
from app import userevents
logger = logging.getLogger(__name__)
@ -41,7 +41,7 @@ def index():
@realtime.route("/user/test")
@require_session_login
def user_test():
evt = userevent.UserEvent('logs.quay.io', current_user.db_user().username)
evt = userevents.get_event(current_user.db_user().username)
evt.publish_event_data('test', {'foo': 2})
return 'OK'
@ -58,5 +58,5 @@ def user_subscribe():
if not events:
abort(404)
listener = userevent.UserEventListener('logs.quay.io', current_user.db_user().username, events)
listener = userevents.get_listener(current_user.db_user().username, events)
return Response(wrapper(listener), mimetype="text/event-stream")

View file

@ -7,9 +7,7 @@ from functools import wraps
from datetime import datetime
from time import time
from data.queue import image_diff_queue
from app import storage as store
from app import storage as store, image_diff_queue
from auth.auth import process_auth, extract_namespace_repo_from_session
from util import checksums, changes
from util.http import abort

View file

@ -8,7 +8,7 @@ from data import model
from auth.auth import process_auth
from auth.permissions import ModifyRepositoryPermission
from util.invoice import renderInvoiceToHtml
from util.email import send_invoice_email, send_subscription_change, send_payment_failed
from util.useremails import send_invoice_email, send_subscription_change, send_payment_failed
from util.names import parse_repository_name
from util.http import abort
from endpoints.trigger import BuildTrigger, ValidationRequestException, SkipRequestException