Add some login machinery.

This commit is contained in:
yackob03 2013-09-23 12:37:40 -04:00
parent 366907b08d
commit e107d79612
10 changed files with 172 additions and 11 deletions

57
api.py Normal file
View file

@ -0,0 +1,57 @@
import logging
from flask import request, make_response, jsonify, abort
from flask.ext.login import login_required, current_user
from functools import wraps
import model
from app import app
from util import parse_repository_name
logger = logging.getLogger(__name__)
@app.route('/api/')
def welcome():
return make_response('welcome', 200)
@app.route('/api/repository/', methods=['POST'])
@login_required
def create_repo_api():
pass
@app.route('/api/repository/', methods=['GET'])
@login_required
def list_repos_api():
def repo_view(repo_perm):
return {
'namespace': repo_perm.repository.namespace,
'name': repo_perm.repository.name,
'role': repo_perm.role.name,
}
repos = [repo_view(repo)
for repo in model.get_user_repositories(current_user)]
response = {
'repositories': repos
}
return jsonify(response)
@app.route('/api/repository/<path:repository>', methods=['PUT'])
@login_required
@parse_repository_name
def update_repo_api(namespace, repository):
pass
@app.route('/api/repository/<path:repository>', methods=['GET'])
@login_required
@parse_repository_name
def get_repo_api(namespace, repository):
pass

47
app.py
View file

@ -1,10 +1,55 @@
from flask import Flask, make_response, jsonify
import logging
from flask import (Flask, make_response, request, abort, render_template,
redirect, url_for)
from flask.ext.principal import Principal
from flask.ext.login import login_user, LoginManager
import model
app = Flask(__name__)
logger = logging.getLogger(__name__)
Principal(app, use_sessions=False)
app.secret_key = '1cb18882-6d12-440d-a4cc-b7430fb5f884'
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = 'signin'
@login_manager.user_loader
def load_user(username):
return model.get_user(username)
@app.route('/', methods=['GET'])
def index():
return render_template('index.html')
@app.route('/signin', methods=['POST'])
def signin():
username = request.form['username']
password = request.form['password']
#TODO Allow email login
verified = model.verify_user(username, password)
if verified:
logger.debug('Successfully signed in as: %s' % username)
login_user(verified)
return redirect(request.args.get('next') or url_for('index'))
abort(403)
@app.route('/signin', methods=['GET'])
def render_signin_page():
return render_template('signin.html')
@app.route('/_ping')
@app.route('/v1/_ping')
def ping():

View file

@ -20,6 +20,18 @@ class User(BaseModel):
email = CharField(unique=True)
verified = BooleanField(default=False)
def is_active(self):
return self.verified
def is_authenticated(self):
return True
def is_anonymous(self):
return False
def get_id(self):
return unicode(self.username)
class Visibility(BaseModel):
name = CharField()

View file

@ -8,26 +8,19 @@ from functools import wraps
from app import app
from auth import process_auth, get_authenticated_user, get_validated_token
from util import parse_namespace_repository
from util import parse_namespace_repository, parse_repository_name
from permissions import (ModifyRepositoryPermission, ReadRepositoryPermission,
UserPermission)
import model
logger = logging.getLogger(__name__)
REGISTRY_SERVER = 'localhost:5003'
def parse_repository_name(f):
@wraps(f)
def wrapper(repository, *args, **kwargs):
(namespace, repository) = parse_namespace_repository(repository)
return f(namespace, repository, *args, **kwargs)
return wrapper
def generate_headers(access):
def add_headers(f):
@wraps(f)

View file

@ -15,6 +15,13 @@ def create_user(username, password, email):
return new_user
def get_user(username):
try:
return User.get(User.username == username)
except User.DoesNotExist:
return None
def verify_user(username, password):
try:
fetched = User.get(User.username == username)
@ -61,6 +68,14 @@ def get_repository(namespace, name):
return None
def get_user_repositories(user):
select = RepositoryPermission.select(RepositoryPermission, Repository, Role)
with_user = select.join(User).where(User.username == user.username)
with_role = with_user.switch(RepositoryPermission).join(Role)
with_repo = with_role.switch(RepositoryPermission).join(Repository)
return with_repo
def create_repository(namespace, name, owner):
private = Visibility.get(name='private')
repo = Repository.create(namespace=namespace, name=name,

View file

@ -2,3 +2,4 @@ peewee
flask
py-bcrypt
Flask-Principal
Flask-Login

15
templates/index.html Normal file
View file

@ -0,0 +1,15 @@
<html>
<head>
<title>Quay - Private Docker Repository</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css">
<!-- Optional theme -->
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap-theme.min.css">
</head>
<body>
<h1>Hello World</h1>
</body>
</html>

12
templates/signin.html Normal file
View file

@ -0,0 +1,12 @@
<html>
<head>
<title>Quay Sign In</title>
</head>
<body>
<form method="post">
Username: <input type="text" name="username"><br>
Password: <input type="password" name="password"><br>
<input type="submit" value="Sign In">
</form>
</body>
</html>

10
util.py
View file

@ -1,5 +1,7 @@
import urllib
from functools import wraps
def parse_namespace_repository(repository):
parts = repository.rstrip('/').split('/', 1)
@ -10,3 +12,11 @@ def parse_namespace_repository(repository):
(namespace, repository) = parts
repository = urllib.quote_plus(repository)
return (namespace, repository)
def parse_repository_name(f):
@wraps(f)
def wrapper(repository, *args, **kwargs):
(namespace, repository) = parse_namespace_repository(repository)
return f(namespace, repository, *args, **kwargs)
return wrapper

View file

@ -3,6 +3,7 @@ import logging
from app import app
import index
import api
if __name__ == '__main__':
FORMAT = '%(asctime)-15s - %(levelname)s - %(pathname)s - ' + \