This repository has been archived by the owner on Dec 23, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #31 from mitodl/rc/v0.1.0
First release
- Loading branch information
Showing
36 changed files
with
2,339 additions
and
23 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
{ | ||
"directory": "lmod_proxy/static/bower/" | ||
} | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
[run] | ||
|
||
branch = True | ||
|
||
[paths] | ||
|
||
source = lmod_proxy | ||
|
||
[report] | ||
exclude_lines = | ||
pragma: no cover |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -52,3 +52,12 @@ docs/_build/ | |
|
||
# PyBuilder | ||
target/ | ||
|
||
node_modules/ | ||
|
||
# App specific | ||
.cert.pem | ||
.htpasswd | ||
|
||
# Heroku | ||
.env |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
language: python | ||
python: | ||
- 2.7 | ||
install: | ||
- pip install -r requirements.txt | ||
- pip install coveralls | ||
script: | ||
- python setup.py test --coverage --pep8 --flakes | ||
- coverage run --source=lmod_proxy setup.py test | ||
after_success: coveralls | ||
deploy: | ||
provider: heroku | ||
api_key: | ||
secure: gHKG6WIKW/MHqTkhdEKZO47SL6Bw1w6kHct/zSwO9fcNA6lLngoTXgJ0AgXdXYoIn5suHgOTM5E1TFRbJ/63tCgGLJgZHkqa0CBZHrp2hK2E2K6rRmGq0QawRfu2QIFN+97VhtLB0pbXzsKYUJRFRWBrLoi62SalxUIJXj8MxJ8= | ||
app: | ||
master: lmod-proxy-ci | ||
on: | ||
repo: mitodl/lmod_proxy |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
include README.rst | ||
include test_requirements.txt |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
web: uwsgi uwsgi.ini |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
lmod_proxy | ||
========== | ||
.. image:: https://img.shields.io/travis/mitodl/lmod_proxy.svg | ||
:target: https://travis-ci.org/mitodl/lmod_proxy | ||
.. image:: https://img.shields.io/coveralls/mitodl/lmod_proxy.svg | ||
:target: https://coveralls.io/r/mitodl/lmod_proxy | ||
.. image:: https://img.shields.io/github/issues/mitodl/lmod_proxy.svg | ||
:target: https://github.com/mitodl/lmod_proxy/issues | ||
.. image:: https://img.shields.io/badge/license-AGPLv3-blue.svg | ||
:target: https://github.com/mitodl/lmod_proxy/blob/master/license | ||
|
||
|Deploy| | ||
|
||
.. |Deploy| image:: https://www.herokucdn.com/deploy/button.png | ||
:target: https://heroku.com/deploy | ||
|
||
Flask application for proxying requests to the MIT Learning Modules | ||
API from edx-platform. | ||
|
||
|
||
Quick Start | ||
=========== | ||
|
||
- Download the latest tar ball or clone from github. | ||
- Run ``pip install -r requirements.txt`` | ||
- Make sure you have ``htpasswd`` available/installed | ||
- Run ``htpasswd -c ~/.htpasswd <your username>`` | ||
- Create environment variable with path to password file: ``export LMODP_HTPASSWD_PATH=~/.htpasswd`` | ||
- Run ``lmod_proxy`` | ||
- Go to https://localhost:5000/ and use the form there to try things out | ||
|
||
You will also likely need to customize your settings to be able to do | ||
authentication to LMod via a proper certificate. To do that, | ||
determine the path to your certificate and export that path for the | ||
configuration with a configuration environment variable: ``export | ||
LMODP_CERT=/path/to/mycert.pem``. The certificate needs to be a plain | ||
(no passphrase) base64 encoded file with both your certificate and | ||
private key from MIT. | ||
|
||
|
||
Running on Heroku | ||
================= | ||
|
||
There is an included Procfile to run the application in heroku, to do | ||
so just create a heroku app, configure the appropriate environment | ||
variables in heroku via ``heroku config:set LMODP_...=...``. The one | ||
catch being that your certificate file and HTPASSWD files probably | ||
shouldn't be checked in with the repository on heroku, so you can | ||
actually copy your entire HTPASSWD file into the ``LMODP_HTPASSWD`` | ||
variable and your certificate into ``LMODP_CERT_STRING`` variable. | ||
|
||
|
||
Configuring edX Platform | ||
======================== | ||
|
||
To configure your edX platform to use this as your remote gradebook | ||
endpoint, you need to configure a few things, first you need to enable | ||
the feature and point it at the URL of your lmod_proxy server. Adding | ||
something like: ``FEATURES['REMOTE_GRADEBOOK_URL'] = | ||
'https://<htpasswd_user>:<htpasswd_password>@myapp.herokuapp.com/edx_grades'`` | ||
to your config will enable the feature, and then in your course, you | ||
will just need to specify the GradeBook UUID to point at in your | ||
advanced settings with something like: | ||
|
||
.. code-block:: json | ||
"remote_gradebook": { | ||
"name": "STELLAR:/project/mitxdemosite", | ||
"section": "devops01" | ||
} | ||
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
{ | ||
"name": "LMod Proxy", | ||
"description": "Proxies grades from Open edX platform to MIT campus Learning Modules", | ||
"keywords": [ | ||
"flask", | ||
"python", | ||
"MIT", | ||
"Learning Modules", | ||
"Open edX", | ||
"Grades" | ||
], | ||
"website": "http://engineering.odl.mit.edu", | ||
"repository": "https://github.com/mitodl/lmod_proxy", | ||
"success_url": "/edx_grades", | ||
"env": { | ||
"LMODP_HTPASSWD": { | ||
"description": "Pasted contents of an htpasswd file" | ||
}, | ||
"LMODP_CERT_STRING": { | ||
"description": "Pasted contents of an unencrypted base64 key and certificate from MIT." | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
{ | ||
"name": "lmod_proxy", | ||
"version": "0.1.0", | ||
"homepage": "https://github.com/mitodl/lmod_proxy", | ||
"authors": [ | ||
"ODL Engineering <odl-engineering@mit.edu>" | ||
], | ||
"description": "Flask application for proxying requests to the MIT Learning Modules API from edx-platform", | ||
"license": "BSD", | ||
"private": true, | ||
"ignore": [ | ||
"**/.*", | ||
"node_modules", | ||
"bower_components", | ||
"test", | ||
"tests" | ||
], | ||
"dependencies": { | ||
"furtive": "~2.0.1", | ||
"jquery2": "~2.0.0" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
# -*- coding: utf-8 -*- | ||
"""lmod_proxy | ||
Flask application for proxying requests to the MIT Learning Modules | ||
API from edx-platform | ||
""" | ||
import os.path | ||
from pkg_resources import get_distribution, DistributionNotFound | ||
|
||
|
||
def _get_version(): | ||
"""Grab version from pkg_resources""" | ||
# pylint: disable=no-member | ||
try: | ||
dist = get_distribution(__project__) | ||
# Normalize case for Windows systems | ||
dist_loc = os.path.normcase(dist.location) | ||
here = os.path.normcase(os.path.abspath(__file__)) | ||
if not here.startswith( | ||
os.path.join(dist_loc, __project__) | ||
): | ||
# not installed, but there is another version that *is* | ||
raise DistributionNotFound | ||
except DistributionNotFound: | ||
return 'Please install this project with setup.py' | ||
else: | ||
return dist.version | ||
|
||
__project__ = 'lmod_proxy' | ||
__version__ = _get_version() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
# -*- coding: utf-8 -*- | ||
"""Basic authentication decorator for views""" | ||
from functools import wraps | ||
import logging | ||
|
||
from flask import current_app, request, Response | ||
|
||
log = logging.getLogger(__name__) # pylint: disable=invalid-name | ||
|
||
|
||
def check_basic_auth(username, password): | ||
""" | ||
This function is called to check if a username / | ||
password combination is valid via the htpasswd file. | ||
""" | ||
valid = current_app.config['users'].check_password(username, password) | ||
if not valid: | ||
log.warn('Invalid login from %s', username) | ||
valid = False | ||
return ( | ||
valid, | ||
username | ||
) | ||
|
||
|
||
def auth_failed(): | ||
""" | ||
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, | ||
{'WWW-Authenticate': 'Basic realm="Login Required"'} | ||
) | ||
|
||
|
||
def requires_auth(func): | ||
""" | ||
Decorator function with basic and token authentication handler | ||
""" | ||
@wraps(func) | ||
def decorated(*args, **kwargs): | ||
""" | ||
Actual wrapper to run the auth checks. | ||
""" | ||
basic_auth = request.authorization | ||
is_valid = False | ||
if basic_auth: | ||
is_valid, user = check_basic_auth( | ||
basic_auth.username, basic_auth.password | ||
) | ||
if not is_valid: | ||
return auth_failed() | ||
kwargs['user'] = user | ||
return func(*args, **kwargs) | ||
return decorated |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
# -*- coding: utf-8 -*- | ||
"""Command line debug start of flask application""" | ||
import os | ||
|
||
from lmod_proxy import config | ||
|
||
|
||
def run_server(): | ||
"""Debug running of the application via flask's app reloader. | ||
Do not use this command to run in production. A WSGI container like | ||
uwsgi or gunicorn should be used. | ||
""" | ||
port = int(os.environ.get('LMODP_PORT', 5000)) | ||
host = os.environ.get('LMODP_HOST', 'localhost') | ||
|
||
# Debug configuration settings | ||
config.FLASK_LOG_LEVEL = 'DEBUG' | ||
|
||
from lmod_proxy.web import app | ||
app.run(debug=True, host=host, port=port) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
# -*- coding: utf-8 -*- | ||
"""Configuration of flask application via environment, or file""" | ||
import os | ||
|
||
import yaml | ||
|
||
CONFIG_PATHS = [ | ||
os.environ.get('LMODP_CONFIG', ''), | ||
os.path.join(os.getcwd(), 'lmod_proxy.yml'), | ||
os.path.join(os.path.expanduser('~'), 'lmod_proxy.yml'), | ||
'/etc/lmod_proxy.yml', | ||
] | ||
|
||
CONFIG_KEYS = { | ||
# Path to base64 encoded, un-passphrased, key and certificate | ||
# combined file | ||
'LMODP_CERT': 'ocw.app.mit.edu-key-and-cert.pem', | ||
|
||
# The base64 encoded string of the certificate as opposed to the | ||
# path. This will trigger the app to write out the certificate on | ||
# startup for environments that are best configured via variables | ||
# only like Heroku. | ||
'LMODP_CERT_STRING': '', | ||
|
||
# Base URL for which the gradebook API lives and accepts | ||
# certificate authentication | ||
'LMODP_URLBASE': 'https://learning-modules.mit.edu:8443/', | ||
|
||
# Any value other than '' or unset enables grade approval in | ||
# Learning Modules so that instructors do no have to do it | ||
# manually for all grades posted in this instance. | ||
'LMODP_APPROVE_GRADES': None, | ||
|
||
# Direct path to apache htpasswd file to use for basic auth | ||
'LMODP_HTPASSWD_PATH': '.htpasswd', | ||
|
||
# Setting that actually contains the strings of the htpasswd file | ||
# for situations like heroku. If set it will write out the string | ||
# to a file for reading on startup. | ||
'LMODP_HTPASSWD': None, | ||
|
||
# Logging level | ||
'FLASK_LOG_LEVEL': 'INFO', | ||
|
||
# Disable WTF CSRF protection | ||
'WTF_CSRF_ENABLED': False, | ||
} | ||
|
||
|
||
def _configure(): | ||
"""Configure the application by trying config file and overriding with | ||
environment variables. | ||
""" | ||
config_file_path = None | ||
fallback_config = {} | ||
for config_path in CONFIG_PATHS: | ||
if os.path.isfile(config_path): | ||
config_file_path = config_path | ||
break | ||
if config_file_path: | ||
with open(config_file_path) as config_file: | ||
fallback_config = yaml.load(config_file) | ||
|
||
configuration = {} | ||
for key, default_value in CONFIG_KEYS.items(): | ||
configuration[key] = os.environ.get( | ||
key, fallback_config.get(key, default_value) | ||
) | ||
|
||
if configuration['LMODP_HTPASSWD']: | ||
configuration['LMODP_HTPASSWD_PATH'] = os.path.abspath('.htpasswd') | ||
with open(configuration['LMODP_HTPASSWD_PATH'], 'w') as wfile: | ||
wfile.write(configuration['LMODP_HTPASSWD']) | ||
|
||
if configuration['LMODP_CERT_STRING']: | ||
configuration['LMODP_CERT'] = os.path.abspath('.cert.pem') | ||
with open(configuration['LMODP_CERT'], 'w') as wfile: | ||
wfile.write(configuration['LMODP_CERT_STRING']) | ||
|
||
return configuration | ||
|
||
globals().update(_configure()) |
Oops, something went wrong.