Merge branch 'master' into git

This commit is contained in:
Jimmy Zelinskie 2015-04-16 17:38:35 -04:00
commit ba2cb08904
268 changed files with 7008 additions and 1535 deletions

32
util/aes.py Normal file
View file

@ -0,0 +1,32 @@
import base64
import hashlib
from Crypto import Random
from Crypto.Cipher import AES
class AESCipher(object):
""" Helper class for encrypting and decrypting data via AES.
Copied From: http://stackoverflow.com/a/21928790
"""
def __init__(self, key):
self.bs = 32
self.key = key
def encrypt(self, raw):
raw = self._pad(raw)
iv = Random.new().read(AES.block_size)
cipher = AES.new(self.key, AES.MODE_CBC, iv)
return base64.b64encode(iv + cipher.encrypt(raw))
def decrypt(self, enc):
enc = base64.b64decode(enc)
iv = enc[:AES.block_size]
cipher = AES.new(self.key, AES.MODE_CBC, iv)
return self._unpad(cipher.decrypt(enc[AES.block_size:])).decode('utf-8')
def _pad(self, s):
return s + (self.bs - len(s) % self.bs) * chr(self.bs - len(s) % self.bs)
@staticmethod
def _unpad(s):
return s[:-ord(s[len(s)-1:])]

View file

@ -6,6 +6,10 @@ from StringIO import StringIO
logger = logging.getLogger(__name__)
class CannotWriteConfigException(Exception):
""" Exception raised when the config cannot be written. """
pass
def _import_yaml(config_obj, config_file):
with open(config_file) as f:
c = yaml.safe_load(f)
@ -24,8 +28,11 @@ def _import_yaml(config_obj, config_file):
def _export_yaml(config_obj, config_file):
with open(config_file, 'w') as f:
f.write(yaml.safe_dump(config_obj, encoding='utf-8', allow_unicode=True))
try:
with open(config_file, 'w') as f:
f.write(yaml.safe_dump(config_obj, encoding='utf-8', allow_unicode=True))
except IOError as ioe:
raise CannotWriteConfigException(str(ioe))
class BaseProvider(object):
@ -116,7 +123,10 @@ class FileConfigProvider(BaseProvider):
return open(os.path.join(self.config_volume, filename), mode)
def save_volume_file(self, filename, flask_file):
flask_file.save(os.path.join(self.config_volume, filename))
try:
flask_file.save(os.path.join(self.config_volume, filename))
except IOError as ioe:
raise CannotWriteConfigException(str(ioe))
def requires_restart(self, app_config):
file_config = self.get_yaml()

View file

@ -122,12 +122,20 @@ def _validate_github_with_key(config_key, config):
if not github_config.get('CLIENT_SECRET'):
raise Exception('Missing Client Secret')
if github_config.get('ORG_RESTRICT') and not github_config.get('ALLOWED_ORGANIZATIONS'):
raise Exception('Organization restriction must have at least one allowed organization')
client = app.config['HTTPCLIENT']
oauth = GithubOAuthConfig(config, config_key)
result = oauth.validate_client_id_and_secret(client)
if not result:
raise Exception('Invalid client id or client secret')
if github_config.get('ALLOWED_ORGANIZATIONS'):
for org_id in github_config.get('ALLOWED_ORGANIZATIONS'):
if not oauth.validate_organization(org_id, client):
raise Exception('Invalid organization: %s' % org_id)
def _validate_google_login(config):
""" Validates the Google Login client ID and secret. """

View file

@ -9,25 +9,29 @@ def icon_path(icon_name):
def icon_image(icon_name):
return '<img src="%s" alt="%s">' % (icon_path(icon_name), icon_name)
def team_reference(teamname):
avatar_html = avatar.get_mail_html(teamname, teamname, 24, 'team')
return "<span>%s <b>%s</b></span>" % (avatar_html, teamname)
def user_reference(username):
user = model.get_namespace_user(username)
if not user:
return username
is_robot = False
if user.robot:
parts = parse_robot_username(username)
user = model.get_namespace_user(parts[0])
return """<span><img src="%s" alt="Robot"> <b>%s</b></span>""" % (icon_path('wrench'), username)
alt = 'Organization' if user.organization else 'User'
avatar_html = avatar.get_mail_html(user.username, user.email, 24,
'org' if user.organization else 'user')
return """
<span>
<img src="%s"
style="vertical-align: middle; margin-left: 6px; margin-right: 4px;" alt="%s">
%s
<b>%s</b>
</span>""" % (avatar.get_url(user.email, 16), alt, username)
</span>""" % (avatar_html, username)
def repository_tag_reference(repository_path_and_tag):
@ -52,12 +56,15 @@ def repository_reference(pair):
if not owner:
return "%s/%s" % (namespace, repository)
avatar_html = avatar.get_mail_html(owner.username, owner.email, 16,
'org' if owner.organization else 'user')
return """
<span style="white-space: nowrap;">
<img src="%s" style="vertical-align: middle; margin-left: 6px; margin-right: 4px;">
%s
<a href="%s/repository/%s/%s">%s/%s</a>
</span>
""" % (avatar.get_url(owner.email, 16), get_app_url(), namespace, repository, namespace, repository)
""" % (avatar_html, get_app_url(), namespace, repository, namespace, repository)
def admin_reference(username):
@ -84,6 +91,7 @@ def get_template_env(searchpath):
def add_filters(template_env):
template_env.filters['icon_image'] = icon_image
template_env.filters['team_reference'] = team_reference
template_env.filters['user_reference'] = user_reference
template_env.filters['admin_reference'] = admin_reference
template_env.filters['repository_reference'] = repository_reference

View file

@ -1,4 +1,5 @@
import urlparse
import github
class OAuthConfig(object):
def __init__(self, config, key_name):
@ -40,12 +41,25 @@ class GithubOAuthConfig(OAuthConfig):
def service_name(self):
return 'GitHub'
def allowed_organizations(self):
if not self.config.get('ORG_RESTRICT', False):
return None
allowed = self.config.get('ALLOWED_ORGANIZATIONS', None)
if allowed is None:
return None
return [org.lower() for org in allowed]
def _endpoint(self):
endpoint = self.config.get('GITHUB_ENDPOINT', 'https://github.com')
if not endpoint.endswith('/'):
endpoint = endpoint + '/'
return endpoint
def is_enterprise(self):
return self._endpoint().find('.github.com') < 0
def authorize_endpoint(self):
return self._get_url(self._endpoint(), '/login/oauth/authorize') + '?'
@ -66,6 +80,10 @@ class GithubOAuthConfig(OAuthConfig):
api_endpoint = self._api_endpoint()
return self._get_url(api_endpoint, 'user/emails')
def orgs_endpoint(self):
api_endpoint = self._api_endpoint()
return self._get_url(api_endpoint, 'user/orgs')
def validate_client_id_and_secret(self, http_client):
# First: Verify that the github endpoint is actually Github by checking for the
# X-GitHub-Request-Id here.
@ -91,11 +109,23 @@ class GithubOAuthConfig(OAuthConfig):
timeout=5)
return result.status_code == 404
def validate_organization(self, organization_id, http_client):
api_endpoint = self._api_endpoint()
org_endpoint = self._get_url(api_endpoint, 'orgs/%s' % organization_id.lower())
result = http_client.get(org_endpoint,
headers={'Accept': 'application/vnd.github.moondragon+json'},
timeout=5)
return result.status_code == 200
def get_public_config(self):
return {
'CLIENT_ID': self.client_id(),
'AUTHORIZE_ENDPOINT': self.authorize_endpoint(),
'GITHUB_ENDPOINT': self._endpoint()
'GITHUB_ENDPOINT': self._endpoint(),
'ORG_RESTRICT': self.config.get('ORG_RESTRICT', False)
}