Skip to content

Commit

Permalink
Merge commit 'f9e1e37'
Browse files Browse the repository at this point in the history
  • Loading branch information
alf committed Oct 10, 2011
2 parents 147a532 + f9e1e37 commit cee9410
Show file tree
Hide file tree
Showing 3 changed files with 171 additions and 4 deletions.
20 changes: 16 additions & 4 deletions __init__.py
@@ -1,11 +1,23 @@
from __future__ import absolute_import

from flask import Blueprint
from flask import jsonify
from flask import url_for

from .auth import requires_auth
from . import v1

api = Blueprint('api', __name__)

@api.route('/')
@requires_auth
def index():
return jsonify(
"description"="REST API for CT",
"version"="1.0"
)
return jsonify({
"description": "REST API for CT",
"links": [{
"rel": "api-version-1.0",
"href": url_for(".v1_0.index")
}]
})

v1.add_routes(api)
77 changes: 77 additions & 0 deletions auth.py
@@ -0,0 +1,77 @@
from __future__ import absolute_import

from functools import wraps
from hashlib import sha256
from flask import request, Response
from flask import session, g

from ct.core.apis import BaseAPI


def get_ct_object(username, password):
key = get_session_key(username, password)
if key in session and session[key].valid_session():
return session[key]

if do_ct_login(username, password):
return get_ct_object(username, password)

return None


def do_ct_login(username, password):
ct = BaseAPI("https://currenttime.bouvet.no")
logged_in = ct.login(username, password)
if logged_in:
key = get_session_key(username, password)
session[key] = ct

return logged_in


def get_session_key(username, password):
return sha256("%s:%s" % (username, password)).hexdigest()


def check_auth(username, password):
"""This function is called to check if a username /
password combination is valid.
"""
return get_ct_object(username, password) is not None


def get_auth_headers():
"""Returns the WWW-Authenticate headers. We use Basic unless the
clients has set the UseXBasic header. In that case we use XBasic
instead. This is because most web browsers insist on showing the
Basic auth dialogue even if the request is done using XHR."""

auth_type = "Basic"
if request.headers.get('UseXBasic'):
auth_type = "XBasic"

return {
'WWW-Authenticate': '%s realm="Login Required"' % auth_type
}


def authenticate():
"""Sends a 401 response that enables basic auth"""

return Response(
'Could not verify your access level for that URL.\n'
'You have to login with proper credentials', 401,
get_auth_headers())


def requires_auth(f):
@wraps(f)
def decorated(*args, **kwargs):
auth = request.authorization
if not auth or not check_auth(auth.username, auth.password):
return authenticate()

g.ct = get_ct_object(auth.username, auth.password)

return f(*args, **kwargs)
return decorated
78 changes: 78 additions & 0 deletions v1.py
@@ -0,0 +1,78 @@
from __future__ import absolute_import

from .auth import requires_auth
from flask import jsonify
from flask import url_for
from flask import g


@requires_auth
def index():
return jsonify({
"description": "REST API for CT version 1.0",
"links": [{
"rel": "available-projects",
"href": url_for('.projects')
}, {
"rel": "activities-by-week",
"href": url_for('.week', year="<year>", week="<week>")
}]
})


def serialize_projects(projects):
result = []
for p in projects:
result.append({
'id': p.id,
'name': p.name,
'project_name': p.project_name,
'task_name': p.task_name,
'subtask_name': p.subtask_name,
'activity_name': p.activity_name,
})
return result


def serialize_activities(activities):
result = []
for activity in activities:
if activity.duration <= 0:
continue

date = activity.day.strftime("%Y-%m-%d")
result.append({
'id': activity.project_id,
'comment': activity.comment,
'duration': str(activity.duration),
'day': date
})
return result


@requires_auth
def get_projects():
projects = serialize_projects(g.ct.get_projects())

return jsonify({
"projects": projects
})


@requires_auth
def get_week(year, week):
activities = serialize_activities(g.ct.get_week(year, week))

return jsonify({
"activities": activities
})


def add_routes(api):
root = 'v1_0'
api.add_url_rule('/v1/',
root + '.index', index)
api.add_url_rule('/v1/projects/',
root + '.projects', get_projects)
api.add_url_rule('/v1/week/<int:year>/<int:week>',
root + '.week', get_week)

0 comments on commit cee9410

Please sign in to comment.