Merge branch 'master' into git
This commit is contained in:
commit
ba2cb08904
268 changed files with 7008 additions and 1535 deletions
32
util/aes.py
Normal file
32
util/aes.py
Normal 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:])]
|
|
@ -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()
|
||||
|
|
|
@ -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. """
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
||||
|
|
Reference in a new issue