예제 #1
0
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
예제 #2
0
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)
예제 #3
0
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
예제 #4
0
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)
예제 #6
0
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)
예제 #7
0
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)
예제 #8
0
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)
예제 #9
0
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)
예제 #10
0
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
예제 #11
0
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)