class FlickrService(object): def __init__(self, app=None): self.app = app self.client = None if app is not None: # pragma: no cover self.init_app(app) def init_app(self, app): # pragma: no cover from flask import session from pybossa.core import importer self.client = OAuth().remote_app( 'flickr', request_token_url='https://www.flickr.com/services/oauth/request_token', access_token_url='https://www.flickr.com/services/oauth/access_token', authorize_url='https://www.flickr.com/services/oauth/authorize', consumer_key=app.config['FLICKR_API_KEY'], consumer_secret=app.config['FLICKR_SHARED_SECRET']) tokengetter = functools.partial(self.get_flickr_token, session) self.client.tokengetter(tokengetter) importer_params = {'api_key': app.config['FLICKR_API_KEY']} importer.register_flickr_importer(importer_params) def get_user_albums(self, session): if (session.get('flickr_user') is not None and session.get('flickr_token') is not None): url = ('https://api.flickr.com/services/rest/?' 'method=flickr.photosets.getList&user_id=%s' '&primary_photo_extras=url_q' '&format=json&nojsoncallback=1' % session.get('flickr_user').get('user_nsid')) res = self.client.get(url) if res.status == 200 and res.data.get('stat') == 'ok': albums = res.data['photosets']['photoset'] return [self._extract_album_info(album) for album in albums] else: msg = ("Bad response from Flickr:\nStatus: %s, Content: %s" % (res.status, res.data)) self.app.logger.error(msg) return [] def authorize(self, *args, **kwargs): return self.client.authorize(*args, **kwargs) def authorized_response(self): return self.client.authorized_response() def get_oauth_client(self): return self.client def get_flickr_token(self, session): return session.get('flickr_token') def _extract_album_info(self, album): info = {'title': album['title']['_content'], 'photos': album['photos'], 'id': album['id'], 'thumbnail_url': album['primary_photo_extras']['url_q']} return info
class GithubAuthenticator(Authenticator): is_oauth = True session_key = "github_token" def __init__(self, app): self.gh = OAuth(app).remote_app( "github", consumer_key=settings.auth_github_key, consumer_secret=settings.auth_github_secret, access_token_method="POST", access_token_url="https://github.com/login/oauth/access_token", authorize_url="https://github.com/login/oauth/authorize", base_url="https://api.github.com/", request_token_params={"scope": "read:org"}, request_token_url=None, ) self.gh.tokengetter(self.get_github_oauth_token) def get_github_oauth_token(self): return flask.session.get(self.session_key) def is_logged_in(self): return bool(flask.session.get(self.session_key)) def is_allowed(self): if settings.auth_github_orgs not in ("all", "*"): org_names = {o["login"] for o in self.gh.get("user/orgs").data} if not _matches_conf(org_names, settings.auth_github_orgs): return False if settings.auth_github_teams not in ("all", "*"): team_names = {o["name"] for o in self.gh.get("user/teams").data} if not _matches_conf(team_names, settings.auth_github_teams): return False return True def login(self): return self.gh.authorize(callback=flask.url_for("authorized", _external=True)) def oauth_authorize(self): resp = self.gh.authorized_response() if not resp or not resp.get("access_token"): flask.abort( 403, "<br>".join( ( "Access denied! Reason: %s" % flask.request.args["error"], "Description: %s" % flask.request.args["error_description"], flask.request.args["error_uri"], ) ), ) flask.session[self.session_key] = (resp["access_token"], "") def logout(self): flask.session.pop(self.session_key, None)
class FlickrService(object): """Class for Flickr integration.""" def __init__(self, app=None): """Init method.""" self.app = app self.client = None if app is not None: # pragma: no cover self.init_app(app) def init_app(self, app): # pragma: no cover """Method to init object following factories pattern.""" from flask import session from pybossa.core import importer self.app = app self.client = OAuth().remote_app( 'flickr', request_token_url='https://www.flickr.com/services/oauth/request_token', access_token_url='https://www.flickr.com/services/oauth/access_token', authorize_url='https://www.flickr.com/services/oauth/authorize', consumer_key=app.config['FLICKR_API_KEY'], consumer_secret=app.config['FLICKR_SHARED_SECRET'], access_token_method='GET') tokengetter = functools.partial(self.get_token, session) self.client.tokengetter(tokengetter) importer_params = {'api_key': app.config['FLICKR_API_KEY']} importer.register_flickr_importer(importer_params) def get_user_albums(self, session): """Get user albums from Flickr.""" if (session.get('flickr_user') is not None and session.get('flickr_token') is not None): url = ('https://api.flickr.com/services/rest/?' 'method=flickr.photosets.getList&user_id=%s' '&primary_photo_extras=url_q' '&format=json&nojsoncallback=1' % self._get_user_nsid(session)) res = self.client.get(url, token='') if res.status == 200 and res.data.get('stat') == 'ok': albums = res.data['photosets']['photoset'] return [self._extract_album_info(album) for album in albums] if self.app is not None: msg = ("Bad response from Flickr:\nStatus: %s, Content: %s" % (res.status, res.data)) self.app.logger.error(msg) return [] def authorize(self, *args, **kwargs): """Authorize method.""" return self.client.authorize(*args, **kwargs) def authorized_response(self): """Authorized response.""" return self.client.authorized_response() def get_oauth_client(self): """Get OAuth client.""" return self.client def get_token(self, session): """Get token from session.""" token = session.get('flickr_token') if token is not None: token = (token['oauth_token'], token['oauth_token_secret']) return token def save_credentials(self, session, token, user): """Save credentials of user in session.""" session['flickr_token'] = token session['flickr_user'] = user def remove_credentials(self, session): """Remove user credentials from session.""" session.pop('flickr_token', None) session.pop('flickr_user', None) def _get_user_nsid(self, session): """Get session ID.""" return session.get('flickr_user').get('user_nsid') def _extract_album_info(self, album): """Extract album information.""" info = {'title': album['title']['_content'], 'photos': album['photos'], 'id': album['id'], 'thumbnail_url': album['primary_photo_extras']['url_q']} return info
class GoogleAuthBackend: def __init__(self): # self.google_host = get_config_param('host') self.login_manager = flask_login.LoginManager() self.login_manager.login_view = 'airflow.login' self.flask_app = None self.google_oauth = None self.api_rev = None def init_app(self, flask_app): self.flask_app = flask_app self.login_manager.init_app(self.flask_app) self.google_oauth = OAuth(self.flask_app).remote_app( 'google', consumer_key=get_config_param('client_id'), consumer_secret=get_config_param('client_secret'), request_token_params={ 'scope': [ 'https://www.googleapis.com/auth/userinfo.profile', 'https://www.googleapis.com/auth/userinfo.email' ] }, base_url='https://www.google.com/accounts/', request_token_url=None, access_token_method='POST', access_token_url='https://accounts.google.com/o/oauth2/token', authorize_url='https://accounts.google.com/o/oauth2/auth') self.login_manager.user_loader(self.load_user) self.flask_app.add_url_rule(get_config_param('oauth_callback_route'), 'google_oauth_callback', self.oauth_callback) def login(self, request): log.debug('Redirecting user to Google login') return self.google_oauth.authorize( callback=url_for('google_oauth_callback', _external=True), state=request.args.get('next') or request.referrer or None) def get_google_user_profile_info(self, google_token): resp = self.google_oauth.get( 'https://www.googleapis.com/oauth2/v1/userinfo', token=(google_token, '')) if not resp or resp.status != 200: raise AuthenticationError( 'Failed to fetch user profile, status ({0})'.format( resp.status if resp else 'None')) return resp.data['name'], resp.data['email'] def domain_check(self, email): domain = email.split('@')[1] domains = get_config_param('domain').split(',') if domain in domains: return True return False @provide_session def load_user(self, userid, session=None): if not userid or userid == 'None': return None user = session.query( models.User).filter(models.User.id == int(userid)).first() return GoogleUser(user) @provide_session def oauth_callback(self, session=None): log.debug('Google OAuth callback called') next_url = request.args.get('state') or url_for('admin.index') resp = self.google_oauth.authorized_response() try: if resp is None: raise AuthenticationError( 'Null response from Google, denying access.') google_token = resp['access_token'] username, email = self.get_google_user_profile_info(google_token) if not self.domain_check(email): return redirect(url_for('airflow.noaccess')) except AuthenticationError: return redirect(url_for('airflow.noaccess')) user = session.query( models.User).filter(models.User.username == username).first() if not user: user = models.User(username=username, email=email, is_superuser=False) session.merge(user) session.commit() login_user(GoogleUser(user)) session.commit() return redirect(next_url)
class GHEAuthBackend(object): def __init__(self): self.ghe_host = get_config_param('host') self.login_manager = flask_login.LoginManager() self.login_manager.login_view = 'airflow.login' self.flask_app = None self.ghe_oauth = None self.api_url = None def ghe_api_route(self, leaf): if not self.api_url: self.api_url = ( 'https://api.github.com' if self.ghe_host == 'github.com' else '/'.join(['https:/', self.ghe_host, 'api', get_config_param('api_rev')]) ) return self.api_url + leaf def init_app(self, flask_app): self.flask_app = flask_app self.login_manager.init_app(self.flask_app) self.ghe_oauth = OAuth(self.flask_app).remote_app( 'ghe', consumer_key=get_config_param('client_id'), consumer_secret=get_config_param('client_secret'), # need read:org to get team member list request_token_params={'scope': 'user:email,read:org'}, base_url=self.ghe_host, request_token_url=None, access_token_method='POST', access_token_url=''.join(['https://', self.ghe_host, '/login/oauth/access_token']), authorize_url=''.join(['https://', self.ghe_host, '/login/oauth/authorize'])) self.login_manager.user_loader(self.load_user) self.flask_app.add_url_rule(get_config_param('oauth_callback_route'), 'ghe_oauth_callback', self.oauth_callback) def login(self, request): log.debug('Redirecting user to GHE login') return self.ghe_oauth.authorize(callback=url_for( 'ghe_oauth_callback', _external=True), state=request.args.get('next') or request.referrer or None) def get_ghe_user_profile_info(self, ghe_token): resp = self.ghe_oauth.get(self.ghe_api_route('/user'), token=(ghe_token, '')) if not resp or resp.status != 200: raise AuthenticationError( 'Failed to fetch user profile, status ({0})'.format( resp.status if resp else 'None')) return resp.data['login'], resp.data['email'] def ghe_team_check(self, username, ghe_token): try: # the response from ghe returns the id of the team as an integer try: allowed_teams = [int(team.strip()) for team in get_config_param('allowed_teams').split(',')] except ValueError: # this is to deprecate using the string name for a team raise ValueError( 'it appears that you are using the string name for a team, ' 'please use the id number instead') except AirflowConfigException: # No allowed teams defined, let anyone in GHE in. return True # https://developer.github.com/v3/orgs/teams/#list-user-teams resp = self.ghe_oauth.get(self.ghe_api_route('/user/teams'), token=(ghe_token, '')) if not resp or resp.status != 200: raise AuthenticationError( 'Bad response from GHE ({0})'.format( resp.status if resp else 'None')) for team in resp.data: # mylons: previously this line used to be if team['slug'] in teams # however, teams are part of organizations. organizations are unique, # but teams are not therefore 'slug' for a team is not necessarily unique. # use id instead if team['id'] in allowed_teams: return True log.debug('Denying access for user "%s", not a member of "%s"', username, str(allowed_teams)) return False @provide_session def load_user(self, userid, session=None): if not userid or userid == 'None': return None user = session.query(models.User).filter( models.User.id == int(userid)).first() return GHEUser(user) @provide_session def oauth_callback(self, session=None): log.debug('GHE OAuth callback called') next_url = request.args.get('state') or url_for('admin.index') resp = self.ghe_oauth.authorized_response() try: if resp is None: raise AuthenticationError( 'Null response from GHE, denying access.' ) ghe_token = resp['access_token'] username, email = self.get_ghe_user_profile_info(ghe_token) if not self.ghe_team_check(username, ghe_token): return redirect(url_for('airflow.noaccess')) except AuthenticationError: log.exception('') return redirect(url_for('airflow.noaccess')) user = session.query(models.User).filter( models.User.username == username).first() if not user: user = models.User( username=username, email=email, is_superuser=False) session.merge(user) session.commit() login_user(GHEUser(user)) session.commit() return redirect(next_url)
class GHEAuthBackend(object): def __init__(self): self.ghe_host = get_config_param('host') self.login_manager = flask_login.LoginManager() self.login_manager.login_view = 'airflow.login' self.flask_app = None self.ghe_oauth = None self.api_rev = None def ghe_api_route(self, leaf): if not self.api_rev: self.api_rev = get_config_param('api_rev') return '/'.join(['https:/', self.ghe_host, 'api', self.api_rev, leaf.strip('/')]) def init_app(self, flask_app): self.flask_app = flask_app self.login_manager.init_app(self.flask_app) self.ghe_oauth = OAuth(self.flask_app).remote_app( 'ghe', consumer_key=get_config_param('client_id'), consumer_secret=get_config_param('client_secret'), # need read:org to get team member list request_token_params={'scope': 'user,read:org'}, base_url=self.ghe_host, request_token_url=None, access_token_method='POST', access_token_url=''.join(['https://', self.ghe_host, '/login/oauth/access_token']), authorize_url=''.join(['https://', self.ghe_host, '/login/oauth/authorize'])) self.login_manager.user_loader(self.load_user) self.flask_app.add_url_rule(get_config_param('oauth_callback_route'), 'ghe_oauth_callback', self.oauth_callback) def login(self, request): _log.debug('Redirecting user to GHE login') return self.ghe_oauth.authorize(callback=url_for( 'ghe_oauth_callback', _external=True, next=request.args.get('next') or request.referrer or None)) def get_ghe_user_profile_info(self, ghe_token): resp = self.ghe_oauth.get(self.ghe_api_route('/user'), token=(ghe_token, '')) if not resp or resp.status != 200: raise AuthenticationError( 'Failed to fetch user profile, status ({0})'.format( resp.status if resp else 'None')) return resp.data['login'], resp.data['email'] def ghe_team_check(self, username, ghe_token): try: teams = [team.strip() for team in get_config_param('allowed_teams').split(',')] except AirflowConfigException: # No allowed teams defined, let anyone in GHE in. return True resp = self.ghe_oauth.get(self.ghe_api_route('/user/teams'), token=(ghe_token, '')) if not resp or resp.status != 200: raise AuthenticationError( 'Bad response from GHE ({0})'.format( resp.status if resp else 'None')) for team in resp.data: # team json object has a slug cased team name field aptly named # 'slug' if team['slug'] in teams: return True _log.debug('Denying access for user "%s", not a member of "%s"', username, str(teams)) return False def load_user(self, userid): if not userid or userid == 'None': return None session = settings.Session() user = session.query(models.User).filter( models.User.id == int(userid)).first() session.expunge_all() session.commit() session.close() return GHEUser(user) def oauth_callback(self): _log.debug('GHE OAuth callback called') next_url = request.args.get('next') or url_for('admin.index') resp = self.ghe_oauth.authorized_response() try: if resp is None: raise AuthenticationError( 'Null response from GHE, denying access.' ) ghe_token = resp['access_token'] username, email = self.get_ghe_user_profile_info(ghe_token) if not self.ghe_team_check(username, ghe_token): return redirect(url_for('airflow.noaccess')) except AuthenticationError: _log.exception('') return redirect(url_for('airflow.noaccess')) session = settings.Session() user = session.query(models.User).filter( models.User.username == username).first() if not user: user = models.User( username=username, email=email, is_superuser=False) session.merge(user) session.commit() login_user(GHEUser(user)) session.commit() session.close() return redirect(next_url)
class GHEAuthBackend(object): def __init__(self): self.ghe_host = get_config_param('host') self.login_manager = flask_login.LoginManager() self.login_manager.login_view = 'airflow.login' self.flask_app = None self.ghe_oauth = None self.api_rev = None def ghe_api_route(self, leaf): if not self.api_rev: self.api_rev = get_config_param('api_rev') return '/'.join( ['https:/', self.ghe_host, 'api', self.api_rev, leaf.strip('/')]) def init_app(self, flask_app): self.flask_app = flask_app self.login_manager.init_app(self.flask_app) self.ghe_oauth = OAuth(self.flask_app).remote_app( 'ghe', consumer_key=get_config_param('client_id'), consumer_secret=get_config_param('client_secret'), # need read:org to get team member list request_token_params={'scope': 'user:email,read:org'}, base_url=self.ghe_host, request_token_url=None, access_token_method='POST', access_token_url=''.join( ['https://', self.ghe_host, '/login/oauth/access_token']), authorize_url=''.join( ['https://', self.ghe_host, '/login/oauth/authorize'])) self.login_manager.user_loader(self.load_user) self.flask_app.add_url_rule(get_config_param('oauth_callback_route'), 'ghe_oauth_callback', self.oauth_callback) def login(self, request): log.debug('Redirecting user to GHE login') return self.ghe_oauth.authorize(callback=url_for('ghe_oauth_callback', _external=True), state=request.args.get('next') or request.referrer or None) def get_ghe_user_profile_info(self, ghe_token): resp = self.ghe_oauth.get(self.ghe_api_route('/user'), token=(ghe_token, '')) if not resp or resp.status != 200: raise AuthenticationError( 'Failed to fetch user profile, status ({0})'.format( resp.status if resp else 'None')) return resp.data['login'], resp.data['email'] def ghe_team_check(self, username, ghe_token): try: # the response from ghe returns the id of the team as an integer try: allowed_teams = [ int(team.strip()) for team in get_config_param('allowed_teams').split(',') ] except ValueError: # this is to deprecate using the string name for a team raise ValueError( 'it appears that you are using the string name for a team, ' 'please use the id number instead') except AirflowConfigException: # No allowed teams defined, let anyone in GHE in. return True # https://developer.github.com/v3/orgs/teams/#list-user-teams resp = self.ghe_oauth.get(self.ghe_api_route('/user/teams'), token=(ghe_token, '')) if not resp or resp.status != 200: raise AuthenticationError('Bad response from GHE ({0})'.format( resp.status if resp else 'None')) for team in resp.data: # mylons: previously this line used to be if team['slug'] in teams # however, teams are part of organizations. organizations are unique, # but teams are not therefore 'slug' for a team is not necessarily unique. # use id instead if team['id'] in allowed_teams: return True log.debug('Denying access for user "%s", not a member of "%s"', username, str(allowed_teams)) return False @provide_session def load_user(self, userid, session=None): if not userid or userid == 'None': return None user = session.query( models.User).filter(models.User.id == int(userid)).first() return GHEUser(user) @provide_session def oauth_callback(self, session=None): log.debug('GHE OAuth callback called') next_url = request.args.get('state') or url_for('admin.index') resp = self.ghe_oauth.authorized_response() try: if resp is None: raise AuthenticationError( 'Null response from GHE, denying access.') ghe_token = resp['access_token'] username, email = self.get_ghe_user_profile_info(ghe_token) if not self.ghe_team_check(username, ghe_token): return redirect(url_for('airflow.noaccess')) except AuthenticationError: log.exception('') return redirect(url_for('airflow.noaccess')) user = session.query( models.User).filter(models.User.username == username).first() if not user: user = models.User(username=username, email=email, is_superuser=False) session.merge(user) session.commit() login_user(GHEUser(user)) session.commit() return redirect(next_url)
class GoogleAuthBackend(object): def __init__(self): # self.google_host = get_config_param('host') self.login_manager = flask_login.LoginManager() self.login_manager.login_view = 'airflow.login' self.flask_app = None self.google_oauth = None self.api_rev = None def init_app(self, flask_app): self.flask_app = flask_app self.login_manager.init_app(self.flask_app) self.google_oauth = OAuth(self.flask_app).remote_app( 'google', consumer_key=get_config_param('client_id'), consumer_secret=get_config_param('client_secret'), request_token_params={'scope': [ 'https://www.googleapis.com/auth/userinfo.profile', 'https://www.googleapis.com/auth/userinfo.email']}, base_url='https://www.google.com/accounts/', request_token_url=None, access_token_method='POST', access_token_url='https://accounts.google.com/o/oauth2/token', authorize_url='https://accounts.google.com/o/oauth2/auth') self.login_manager.user_loader(self.load_user) self.flask_app.add_url_rule(get_config_param('oauth_callback_route'), 'google_oauth_callback', self.oauth_callback) def login(self, request): log.debug('Redirecting user to Google login') return self.google_oauth.authorize(callback=url_for( 'google_oauth_callback', _external=True, _scheme='https'), state=request.args.get('next') or request.referrer or None) def get_google_user_profile_info(self, google_token): resp = self.google_oauth.get( 'https://www.googleapis.com/oauth2/v1/userinfo', token=(google_token, '')) if not resp or resp.status != 200: raise AuthenticationError( 'Failed to fetch user profile, status ({0})'.format( resp.status if resp else 'None')) return resp.data['name'], resp.data['email'] def domain_check(self, email): domain = email.split('@')[1] domains = get_config_param('domain').split(',') if domain in domains: return True return False @provide_session def load_user(self, userid, session=None): if not userid or userid == 'None': return None user = session.query(models.User).filter( models.User.id == int(userid)).first() return GoogleUser(user) @provide_session def oauth_callback(self, session=None): log.debug('Google OAuth callback called') next_url = request.args.get('state') or url_for('admin.index') resp = self.google_oauth.authorized_response() try: if resp is None: raise AuthenticationError( 'Null response from Google, denying access.' ) google_token = resp['access_token'] username, email = self.get_google_user_profile_info(google_token) if not self.domain_check(email): return redirect(url_for('airflow.noaccess')) except AuthenticationError: return redirect(url_for('airflow.noaccess')) user = session.query(models.User).filter( models.User.username == username).first() if not user: user = models.User( username=username, email=email, is_superuser=False) session.merge(user) session.commit() login_user(GoogleUser(user)) session.commit() return redirect(next_url)
class AuthbrokerBackend(object): def __init__(self): self.login_manager = flask_login.LoginManager() self.login_manager.login_view = 'airflow.login' self.flask_app = None self.auth_path = 'o/authorize/' self.token_path = 'o/token/' self.me_path = 'api/v1/user/me/' self.client_id = constants.AUTHBROKER_CLIENT_ID self.client_secret = constants.AUTHBROKER_CLIENT_SECRET self.allowed_domains = constants.AUTHBROKER_ALLOWED_DOMAINS.split(',') self.base_url = constants.AUTHBROKER_URL def init_app(self, flask_app): self.flask_app = flask_app self.login_manager.init_app(self.flask_app) self.authbroker_client = OAuth(self.flask_app).remote_app( 'authbroker', base_url=self.base_url, request_token_url=None, access_token_url=f'{self.base_url}{self.token_path}', authorize_url=f'{self.base_url}{self.auth_path}', consumer_key=self.client_id, consumer_secret=self.client_secret, access_token_method='POST', request_token_params={'state': lambda: security.gen_salt(10)}) self.login_manager.user_loader(self.load_user) self.flask_app.add_url_rule('/oauth2callback', 'oauth2callback', self.oauth2callback) def login(self, request): log.info('================Redirecting===================') return self.authbroker_client.authorize( callback=url_for('oauth2callback', _external=True), state=request.args.get('next') or request.referrer or None) def get_user_profile_email(self, authbroker_token): log.info('================Getting user porfile===================') resp = self.authbroker_client.get(f'{self.base_url}{self.me_path}', token=(authbroker_token, '')) if not resp or resp.status != 200: log.info('user profile repsponse failed') raise AuthenticationError( 'Failed to fetch user profile, status ({0})'.format( resp.status if resp else 'None')) log.info('user profile resp ========= {}'.format(resp.__dict__)) return resp.data['email'] def domain_check(self, email): domain = email.split('@')[1] log.info('======DOMAIN {} ==========='.format(domain)) log.info('======ALLOWED DOMAIN {} ========'.format( self.allowed_domains)) if domain in self.allowed_domains: return True return False @provide_session def load_user(self, userid, session=None): if not userid or userid == 'None': return None user = session.query( models.User).filter(models.User.id == int(userid)).first() return StaffUser(user) @provide_session def oauth2callback(self, session=None): log.debug('Authbroker callback called') next_url = request.args.get('state') or url_for('admin.index') resp = self.authbroker_client.authorized_response() try: if resp is None or resp.get('access_token') is None: log.info('===========Couldnt fetch access token=============') raise AuthenticationError( 'Access denied: reason={0} error={1} resp={2}'.format( request.args['error'], request.args['error_description'], resp)) authbroker_token = resp['access_token'] email = self.get_user_profile_email(authbroker_token) if not self.domain_check(email): log.info('==========Domain check failed ==================') return redirect(url_for('airflow.noaccess')) except AuthenticationError: log.info('================Redirecting===================') return redirect(url_for('airflow.noaccess')) user = session.query( models.User).filter(models.User.username == email).first() if not user: user = models.User(username=email, email=email, is_superuser=False) session.merge(user) session.commit() login_user(StaffUser(user)) session.commit() return redirect(next_url)
class FlickrClient(object): """Class for Flickr integration.""" def __init__(self, app=None): """Init method.""" self.app = app self.oauth_client = None if app is not None: # pragma: no cover self.init_app(app) def init_app(self, app): # pragma: no cover """Method to init object following factories pattern.""" from flask import session self.app = app self.oauth_client = OAuth().remote_app( 'flickr', request_token_url='https://www.flickr.com/services/oauth/request_token', access_token_url='https://www.flickr.com/services/oauth/access_token', authorize_url='https://www.flickr.com/services/oauth/authorize', consumer_key=app.config['FLICKR_API_KEY'], consumer_secret=app.config['FLICKR_SHARED_SECRET'], access_token_method='GET') tokengetter = functools.partial(self.get_token, session) self.oauth_client.tokengetter(tokengetter) def get_user_albums(self, session): """Get user albums from Flickr.""" if (session.get('flickr_user') is not None and session.get('flickr_token') is not None): url = ('https://api.flickr.com/services/rest/?' 'method=flickr.photosets.getList&user_id=%s' '&primary_photo_extras=url_q' '&format=json&nojsoncallback=1' % self._get_user_nsid(session)) res = self.oauth_client.get(url, token='') if res.status == 200 and res.data.get('stat') == 'ok': albums = res.data['photosets']['photoset'] return [self._extract_album_info(album) for album in albums] if self.app is not None: msg = ("Bad response from Flickr:\nStatus: %s, Content: %s" % (res.status, res.data)) self.app.logger.error(msg) return [] def authorize(self, *args, **kwargs): """Authorize method.""" return self.oauth_client.authorize(*args, **kwargs) def authorized_response(self): """Authorized response.""" return self.oauth_client.authorized_response() def get_oauth_client(self): """Get OAuth client.""" return self.oauth_client def get_token(self, session): """Get token from session.""" token = session.get('flickr_token') if token is not None: token = (token['oauth_token'], token['oauth_token_secret']) return token def save_credentials(self, session, token, user): """Save credentials of user in session.""" session['flickr_token'] = token session['flickr_user'] = user def remove_credentials(self, session): """Remove user credentials from session.""" session.pop('flickr_token', None) session.pop('flickr_user', None) def _get_user_nsid(self, session): """Get session ID.""" return session.get('flickr_user').get('user_nsid') def _extract_album_info(self, album): """Extract album information.""" info = {'title': album['title']['_content'], 'photos': album['photos'], 'id': album['id'], 'thumbnail_url': album['primary_photo_extras']['url_q']} return info
class OAuthBackend(object): def __init__(self): self.login_manager = flask_login.LoginManager() self.login_manager.login_view = 'airflow.login' self.login_manager.login_message = None self.flask_app = None self.oauth = None self.api_rev = None def init_app(self, flask_app): self.flask_app = flask_app self.login_manager.init_app(self.flask_app) self.oauth = OAuth(self.flask_app).remote_app( 'oauth', consumer_key=get_config_param('client_id'), consumer_secret=get_config_param('client_secret'), base_url=get_config_param('base_url'), request_token_params={'scope': ["user:info", "user:check-access"]}, request_token_url=None, access_token_method=get_config_param('access_token_method'), access_token_url=get_config_param('access_token_url'), authorize_url=get_config_param('authorize_url')) self.login_manager.user_loader(self.load_user) self.flask_app.add_url_rule(get_config_param('oauth_callback_route'), 'oauth_callback', self.oauth_callback) def login(self, request): log.debug('Redirecting user to OAuth login') scheme = request.environ['HTTP_X_FORWARDED_PROTO'] \ if 'HTTP_X_FORWARDED_PROTO' in request.environ and request.environ['HTTP_X_FORWARDED_PROTO'] \ else request.scheme if request.scheme \ else None return self.oauth.authorize(callback=url_for('oauth_callback', _scheme=scheme, _external=True), state=request.args.get('next') or request.referrer or None) def get_user_profile_info(self, access_token): resp = self.oauth.get(get_config_param("user_info_url"), token=(access_token, '')) if not resp or resp.status != 200: raise AuthenticationError( 'Failed to fetch user profile, status ({0})'.format( resp.status if resp else 'None')) return resp.data def dict_get(self, dic, key): keys = key.split(".") value = dic for k in keys: value = value[k] return value @provide_session def load_user(self, userid, session=None): if not userid or userid == 'None': return None user = session.query( models.User).filter(models.User.id == int(userid)).first() return OAuthUser(user) def authorize(self, authorized_response, user_info): """ Parameters ---------- authorized_response Authorized response from OAuth client user_info: dict User information response from OAuth client Returns ------- (bool, bool, bool) Return if 1. the user is allowed to access airflow, 2. if the user is a superuser """ if has_config_param("oauth_permission_backend"): permission_backend = import_module( get_config_param("oauth_permission_backend")) return permission_backend.authorize(self.oauth, authorized_response, user_info) return True, True @provide_session def oauth_callback(self, session=None): log.debug('OAuth callback called') next_url = request.args.get('state') or url_for('admin.index') if get_config_param('base_url') in next_url: next_url = url_for('admin.index') resp = self.oauth.authorized_response() try: if resp is None: raise AuthenticationError( 'Null response from OAuth service, denying access.') access_token = resp['access_token'] user_info = self.get_user_profile_info(access_token) username_key = get_config_param("username_key") email_key = get_config_param("email_key") username = self.dict_get(user_info, username_key) email = self.dict_get(user_info, email_key) authorized, superuser = self.authorize(resp, user_info) except AuthenticationError: return redirect(url_for('airflow.noaccess')) user = session.query( models.User).filter(models.User.username == username).first() if not authorized: if user: session.delete(user) session.commit() return redirect(url_for('airflow.noaccess')) if not user: user = models.User(username=username, email=email, superuser=superuser) user.superuser = superuser session.merge(user) session.commit() login_user(OAuthUser(user)) session.commit() return redirect(next_url)
class GHEAuthBackend(object): def __init__(self): self.ghe_host = get_config_param('host') self.login_manager = flask_login.LoginManager() self.login_manager.login_view = 'airflow.login' self.flask_app = None self.ghe_oauth = None def ghe_api_route(self, leaf): return '/'.join([ 'http:/', self.ghe_host, 'auth/realms/brent/protocol/openid-connect' ]) def init_app(self, flask_app): self.flask_app = flask_app self.login_manager.init_app(self.flask_app) self.ghe_oauth = OAuth(self.flask_app).remote_app( 'ghe', consumer_key=get_config_param('client_id'), consumer_secret=get_config_param('client_secret'), # need read:org to get team member list request_token_params={'scope': 'openid'}, base_url=self.ghe_host, request_token_url=None, access_token_method='POST', access_token_url=''.join([ 'http://', self.ghe_host, '/auth/realms/brent/protocol/openid-connect/token' ]), authorize_url=''.join([ 'http://', self.ghe_host, '/auth/realms/brent/protocol/openid-connect/auth' ])) self.login_manager.user_loader(self.load_user) self.flask_app.add_url_rule(get_config_param('oauth_callback_route'), 'ghe_oauth_callback', self.oauth_callback) def login(self, request): _log.debug('Redirecting user to GHE login') return self.ghe_oauth.authorize(callback=url_for( 'ghe_oauth_callback', _external=True, next=request.args.get('next') or request.referrer or None)) def get_ghe_user_profile_info(self, ghe_token): userinfo_url = ''.join([ 'http://', self.ghe_host, '/auth/realms/brent/protocol/openid-connect/userinfo' ]) resp = self.ghe_oauth.get(userinfo_url, token=(ghe_token, '')) if not resp or resp.status != 200: raise AuthenticationError( 'Failed to fetch user profile, status ({0})'.format( resp.status if resp else 'None')) return resp.data['preferred_username'], resp.data['email'] def load_user(self, userid): if not userid or userid == 'None': return None session = settings.Session() user = session.query( models.User).filter(models.User.id == int(userid)).first() session.expunge_all() session.commit() session.close() return KeyCloakUser(user) def oauth_callback(self): _log.debug('GHE OAuth callback called') next_url = request.args.get('next') or url_for('admin.index') resp = self.ghe_oauth.authorized_response() try: if resp is None: raise AuthenticationError( 'Null response from GHE, denying access.') ghe_token = resp['access_token'] username, email = self.get_ghe_user_profile_info(ghe_token) except AuthenticationError: return redirect(url_for('airflow.noaccess')) session = settings.Session() user = session.query( models.User).filter(models.User.username == username).first() if not user: user = models.User(username=username, email=email, is_superuser=False) session.merge(user) session.commit() login_user(KeyCloakUser(user)) session.commit() session.close() return redirect(next_url)