diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a2c4f8..d80c506 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## [Unreleased] + +### Added + +- Support for GitHub OAuth authorization ([#320](https://github.com/src-d/sourced-ui/issues/320)) + ## [v0.8.1](https://github.com/src-d/sourced-ui/releases/tag/v0.8.1) - 2019-10-28 ### Fixed diff --git a/README.md b/README.md index 6ad4fd1..25696a6 100644 --- a/README.md +++ b/README.md @@ -51,10 +51,12 @@ You can configure the Docker image using the following environment variables: | `METADATA_USER` | Username for metadata DB (when `SYNC_MODE` is set to `true`) | | `METADATA_PASSWORD` | Password for metadata DB (when `SYNC_MODE` is set to `true`) | | `METADATA_DB` | Database name for metadata (when `SYNC_MODE` is set to `true`) | -| `OAUTH_PROVIDER` | Use OAuth provider for authorization. Currently only `google` | -| `OAUTH_CONSUMER_KEY` | OAuth provider consumer key (aka client_id) | -| `OAUTH_CONSUMER_SECRET` | OAuth provider consumer secret (aka client_secret) | +| `OAUTH_ENABLED_PROVIDERS` | Comma separated list of available OAuth providers (eg: `github,google`) | | `OAUTH_REGISTRATION_ROLE` | The role for newly registered users using OAuth `Admin`/`Alpha`/`Gamma` | +| `OAUTH_GITHUB_CONSUMER_KEY` | GitHub OAuth provider consumer key (aka client_id) | +| `OAUTH_GITHUB_CONSUMER_SECRET` | GitHub OAuth provider consumer secret (aka client_secret) | +| `OAUTH_GOOGLE_CONSUMER_KEY` | Google OAuth provider consumer key (aka client_id) | +| `OAUTH_GOOGLE_CONSUMER_SECRET` | Google OAuth provider consumer secret (aka client_secret) | To see the differences between roles in `OAUTH_REGISTRATION_ROLE` variable consult [official superset documentation](https://superset.incubator.apache.org/security.html#provided-roles). diff --git a/srcd/contrib/docker/docker-compose.override.yml b/srcd/contrib/docker/docker-compose.override.yml index e2bf8fc..ac0bdc9 100644 --- a/srcd/contrib/docker/docker-compose.override.yml +++ b/srcd/contrib/docker/docker-compose.override.yml @@ -12,6 +12,7 @@ services: - ${SOURCED_UI_ABS_PATH}/superset/dashboards:/home/superset/dashboards - ${SOURCED_UI_ABS_PATH}/superset/contrib/docker/superset_config.py:/home/superset/superset/superset_config.py - ${SOURCED_UI_ABS_PATH}/superset/contrib/docker/docker-entrypoint.sh:/entrypoint.sh + - ${SOURCED_UI_ABS_PATH}/superset/contrib/docker/bootstrap.py:/home/superset/bootstrap.py - type: volume source: node_modules target: /home/superset/superset/assets/node_modules @@ -21,6 +22,7 @@ services: sourced-ui-celery: # disable separate celery container + restart: \"no\" image: tianon/true entrypoint: /true diff --git a/superset/contrib/docker/docker-entrypoint.sh b/superset/contrib/docker/docker-entrypoint.sh index 4838c83..daf2029 100755 --- a/superset/contrib/docker/docker-entrypoint.sh +++ b/superset/contrib/docker/docker-entrypoint.sh @@ -23,6 +23,7 @@ if [ "`whoami`" = "root" ] && [ -n "$LOCAL_USER" ]; then # It will grant write access to the data from the volumes, # inside of the container and on host file system (see #221) find . -group superset -exec chgrp $LOCAL_USER '{}' \; + find . -group root -exec chgrp $LOCAL_USER '{}' \; groupmod -g $LOCAL_USER superset usermod -u $LOCAL_USER superset chown -R superset superset/assets/node_modules @@ -44,7 +45,7 @@ elif [ "$SUPERSET_ENV" = "development" ]; then # non-asset requests to `Flask`, wich is listening at the port 8081 # Doing so, updates to asset sources will be reflected in-browser without a refresh. (cd superset/assets/ && npm install) - (cd superset/assets/ && npm run dev-server -- --host=0.0.0.0 --port=8088 --supersetPort=8081) & + (cd superset/assets/ && npm run dev-server -- --host=0.0.0.0 --port=8088 --supersetPort=8081 --disable-host-check) & FLASK_ENV=development FLASK_APP=superset:app flask run -p 8081 --with-threads --reload --debugger --host=0.0.0.0 elif [ "$SUPERSET_ENV" = "production" ]; then exec gunicorn --bind 0.0.0.0:8088 \ diff --git a/superset/contrib/docker/superset_config.py b/superset/contrib/docker/superset_config.py index 7283734..ebf6efa 100644 --- a/superset/contrib/docker/superset_config.py +++ b/superset/contrib/docker/superset_config.py @@ -154,31 +154,63 @@ def mutator(f): SUPERSET_WEBSERVER_TIMEOUT = 300 # Authorization configuration - -OAUTH_PROVIDER = get_env_variable('OAUTH_PROVIDER', False) -if OAUTH_PROVIDER: - OAUTH_PROVIDERS = [ - { - 'name': 'google', - 'icon': 'fa-google', - 'token_key': 'access_token', - 'remote_app': { - 'consumer_key': get_env_variable('OAUTH_CONSUMER_KEY'), - 'consumer_secret': get_env_variable('OAUTH_CONSUMER_SECRET'), - 'base_url': 'https://www.googleapis.com/oauth2/v2/', - 'request_token_params': { - 'scope': 'email profile' - }, - 'request_token_url': None, - 'access_token_url': 'https://accounts.google.com/o/oauth2/token', - 'authorize_url': 'https://accounts.google.com/o/oauth2/auth' - } +OAUTH_ENABLED_PROVIDERS = get_env_variable('OAUTH_ENABLED_PROVIDERS', False) +OAUTH_GOOGLE_CONSUMER_KEY = get_env_variable('OAUTH_GOOGLE_CONSUMER_KEY', False) +OAUTH_GOOGLE_CONSUMER_SECRET = get_env_variable('OAUTH_GOOGLE_CONSUMER_SECRET', False) +OAUTH_GITHUB_CONSUMER_KEY = get_env_variable('OAUTH_GITHUB_CONSUMER_KEY', False) +OAUTH_GITHUB_CONSUMER_SECRET = get_env_variable('OAUTH_GITHUB_CONSUMER_SECRET', False) + +OAUTH_AVAILABLE_CONFIGS = { + 'google': { + 'name': 'google', + 'icon': 'fa-google', + 'token_key': 'access_token', + 'remote_app': { + 'consumer_key': OAUTH_GOOGLE_CONSUMER_KEY, + 'consumer_secret': OAUTH_GOOGLE_CONSUMER_SECRET, + 'base_url': 'https://www.googleapis.com/oauth2/v2/', + 'request_token_params': { + 'scope': 'email profile' + }, + 'request_token_url': None, + 'access_token_url': 'https://accounts.google.com/o/oauth2/token', + 'authorize_url': 'https://accounts.google.com/o/oauth2/auth' + } + }, + 'github': { + 'name': 'github', + 'icon': 'fa-github', + 'token_key': 'access_token', + 'remote_app': { + 'consumer_key': OAUTH_GITHUB_CONSUMER_KEY, + 'consumer_secret': OAUTH_GITHUB_CONSUMER_SECRET, + 'base_url': 'https://api.github.com/', + 'request_token_params': { + 'scope': 'user' # read:user + }, + 'request_token_url': None, + 'access_token_method': 'POST', + 'access_token_url': 'https://github.com/login/oauth/access_token', + 'authorize_url': 'https://github.com/login/oauth/authorize' } - ] + } +} + +if OAUTH_ENABLED_PROVIDERS: + providers = [] + provider_names = OAUTH_ENABLED_PROVIDERS.split(',') + for provider in provider_names: + if provider in OAUTH_AVAILABLE_CONFIGS: + if not OAUTH_AVAILABLE_CONFIGS[provider]['remote_app']['consumer_key']: + raise EnvironmentError('Not valid OAuth consumer_key provided for {}'.format(provider)) + if not OAUTH_AVAILABLE_CONFIGS[provider]['remote_app']['consumer_secret']: + raise EnvironmentError('Not valid OAuth consumer_secret provided for {}'.format(provider)) + else: + raise EnvironmentError('Unknown OAuth provider {}'.format(provider)) + + providers.append(OAUTH_AVAILABLE_CONFIGS[provider]) - if OAUTH_PROVIDER not in [p['name'] for p in OAUTH_PROVIDERS]: - raise EnvironmentError( - 'Unknown OAuth provider {}'.format(OAUTH_PROVIDER)) + OAUTH_PROVIDERS = providers from flask_appbuilder.security.manager import AUTH_OAUTH diff --git a/watcher b/watcher index 6d6bd5b..363fa12 100755 --- a/watcher +++ b/watcher @@ -10,15 +10,45 @@ function ctrl_c { } function watch_inotify { - inotifywait --recursive \ - --event modify,move,create,delete \ - $DIRECTORY_TO_OBSERVE + inotifywait --recursive --monitor --quiet \ + --event modify,move,create,delete,attrib \ + ${DIRECTORY_TO_OBSERVE} | awk -v SRCD="^srcd" -v SUPERSET="$(pwd)/superset" '{ \ + print $0; \ + if ($2 ~ "DELETE" || $2 ~ "MOVED_FROM") { \ + print "[deleted]: " $1$3; \ + print "rm -rf " gensub(SRCD, SUPERSET, 1, $1$3); \ + system("rm -rf " gensub(SRCD, SUPERSET, 1, $1$3)); \ + } else if ($1$3 ~ "docker-compose.override.yml") { \ + print "[docker-compose.override]: updated " $1$3; \ + system("cp -rf " $1$3 " ~/.sourced/compose-files/__active__/docker-compose.override.yml"); \ + } \ + else { \ + print "[modified]: " $1$3; \ + system("make --no-print-directory apply-patch"); \ + } \ + }'; } function watch_fswatch { - fswatch --recursive --one-event \ - --event Created --event Updated --event Removed \ - ${DIRECTORY_TO_OBSERVE} + fswatch --recursive --event-flags \ + --event Created --event Updated --event Removed --event Renamed \ + --event MovedFrom --event MovedTo \ + --event OwnerModified --event AttributeModified \ + ${DIRECTORY_TO_OBSERVE} | awk -v SRCD="$(pwd)/srcd" -v SUPERSET="$(pwd)/superset" '{ \ + print $0; \ + if ($2 ~ "Removed") { \ + print "[deleted]: " $1; \ + print "rm -rf " gensub(SRCD, SUPERSET, 1, $1); \ + system("rm -rf " gensub(SRCD, SUPERSET, 1, $1)); \ + } else if ($1 ~ "docker-compose.override.yml") { \ + print "[docker-compose.override]: updated " $1; \ + system("cp -rf " $1 " ~/.sourced/compose-files/__active__/docker-compose.override.yml"); \ + } \ + else { \ + print "[modified]: " $1; \ + system("make --no-print-directory apply-patch"); \ + } \ + }'; } @@ -43,6 +73,5 @@ fi make --no-print-directory apply-patch echo -e "\033[1;92mWatching for changes in 'srcd'; using '${whichWatcher}' ...\033[0m" -while watcher; do - make --no-print-directory apply-patch -done + +watcher