예제 #1
0
    def init_app(self,
                 provider_metadata_extras=None,
                 client_metadata_extras=None,
                 **kwargs):
        required_provider_metadata = {
            'issuer': self.PROVIDER_BASEURL,
            'authorization_endpoint': self.PROVIDER_BASEURL + '/auth',
            'jwks_uri': self.PROVIDER_BASEURL + '/jwks'
        }
        if provider_metadata_extras:
            required_provider_metadata.update(provider_metadata_extras)
        provider_metadata = ProviderMetadata(**required_provider_metadata)

        required_client_metadata = {
            'client_id': self.CLIENT_ID,
            'client_secret': 'secret1'
        }
        if client_metadata_extras:
            required_client_metadata.update(client_metadata_extras)
        client_metadata = ClientMetadata(**required_client_metadata)

        provider_configurations = {
            self.PROVIDER_NAME:
            ProviderConfiguration(provider_metadata=provider_metadata,
                                  client_metadata=client_metadata,
                                  **kwargs)
        }
        authn = OIDCAuthentication(provider_configurations)
        authn.init_app(self.app)
        return authn
예제 #2
0
def get_client_metadata():
    client_id = os.getenv('CLIENT_ID')
    client_secret = os.getenv('CLIENT_SECRET')
    if client_id == None or client_secret == None:
        return None
    else:
        return ClientMetadata(client_id, client_secret)
예제 #3
0
    def init_app(self,
                 provider_metadata_extras=None,
                 client_metadata_extras=None,
                 **kwargs):
        required_provider_metadata = {
            "issuer": self.PROVIDER_BASEURL,
            "authorization_endpoint": self.PROVIDER_BASEURL + "/auth",
            "jwks_uri": self.PROVIDER_BASEURL + "/jwks",
        }
        if provider_metadata_extras:
            required_provider_metadata.update(provider_metadata_extras)
        provider_metadata = ProviderMetadata(**required_provider_metadata)

        required_client_metadata = {
            "client_id": self.CLIENT_ID,
            "client_secret": "secret1"
        }
        if client_metadata_extras:
            required_client_metadata.update(client_metadata_extras)
        client_metadata = ClientMetadata(**required_client_metadata)

        provider_configurations = {
            self.PROVIDER_NAME:
            ProviderConfiguration(provider_metadata=provider_metadata,
                                  client_metadata=client_metadata,
                                  **kwargs)
        }
        authn = OIDCAuthentication(provider_configurations)
        authn.init_app(self.app)
        return authn
예제 #4
0
 def test_should_not_register_dynamic_client_if_client_metadata_is_given(
         self):
     client_metadata = ClientMetadata(client_id='client1',
                                      client_secret='secret1')
     provider_config = ProviderConfiguration(
         provider_metadata=self.provider_metadata(),
         client_metadata=client_metadata)
     provider_config.register_client(None)
     assert provider_config._client_metadata == client_metadata
예제 #5
0
 def test_should_not_register_dynamic_client_if_client_metadata_is_given(
         self):
     client_metadata = ClientMetadata(
         client_id='client1',
         client_secret='secret1',
         redirect_uris=['https://client.example.com/redirect'])
     provider_config = ProviderConfiguration(
         provider_metadata=self.provider_metadata(),
         client_metadata=client_metadata)
     provider_config.register_client([])
     assert provider_config._client_metadata == client_metadata
예제 #6
0
def parse_oidc_config(logout_uri):
    oidc_config = dict()
    with open('provider.json') as provider_file:
        provider_data = json.load(provider_file)
        for provider in provider_data:
            config = ProviderConfiguration(
                issuer=provider_data[provider]['issuer'],
                client_metadata=ClientMetadata(
                    provider_data[provider]['client_id'],
                    provider_data[provider]['client_secret'],
                    post_logout_redirect_uris=[logout_uri + '/logout']))
            oidc_config.update({provider: config})
    return oidc_config
 def test_should_detect_nonce_mismatch(self, client_mock):
     client = PyoidcFacade(
         ProviderConfiguration(
             provider_metadata=ProviderMetadata(issuer=self.ISSUER),
             client_metadata=ClientMetadata(client_id=self.CLIENT_ID)),
         redirect_uri='https://client.example.com/redirect')
     client.exchange_authorization_code = MagicMock(
         return_value=self.TOKEN_RESPONSE)
     auth_request = {
         'state': self.AUTH_RESPONSE['state'],
         'nonce': 'other_nonce'
     }
     with pytest.raises(InvalidIdTokenError):
         AuthResponseHandler(client).process_auth_response(
             self.AUTH_RESPONSE, auth_request)
예제 #8
0
    'SERVER_NAME': 'localhost:5000',
    'SECRET_KEY': 'dev_key',  # make sure to change this!!
    'PERMANENT_SESSION_LIFETIME': 86400,
    'PREFERRED_URL_SCHEME': 'http',
    'DEBUG': True,
    'SQLALCHEMY_DATABASE_URI': 'sqlite:///test.db'
})

# app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///test.db'
db = SQLAlchemy(app)

ISSUER1 = 'http://*****:*****@app.route('/', methods=['POST', 'GET'])
# @auth.oidc_auth(PROVIDER_NAME1)
예제 #9
0
from os import environ as env
from flask import Flask, jsonify, render_template, redirect, session
from flask_pyoidc import OIDCAuthentication
from flask_pyoidc.provider_configuration import ProviderConfiguration, ClientMetadata
from flask_pyoidc.user_session import UserSession
from dotenv import load_dotenv
load_dotenv()

app = Flask(__name__)
app.config.update(
    OIDC_REDIRECT_URI = 'http://*****:*****@app.route('/login')
@auth.oidc_auth('default')
def login():
    user_session = UserSession(session, 'default')
    return redirect('/')

@app.route('/logout')
@auth.oidc_logout
예제 #10
0
# OpenID Connect configuration
app.config.update({'OIDC_REDIRECT_URI': APP_BASE_URI + '/' + 'redirect_uri'})

# Flask web framework configuration
# See http://flask.pocoo.org/docs/0.12/config/
app.config.update({
    'SECRET_KEY':
    'flask_session_key',  # make sure to change this!!
    'JSONIFY_PRETTYPRINT_REGULAR':
    True,
    'PERMANENT_SESSION_LIFETIME':
    datetime.timedelta(minutes=1).total_seconds(),
})

auth_provider_config = ProviderConfiguration(issuer=AUTH_PROVIDER_BASE_URI,
                                             client_metadata=ClientMetadata(
                                                 APP_CLIENT_ID, APP_SECRET))
auth = OIDCAuthentication({AUTH_PROVIDER_NAME: auth_provider_config})


def get_my_groups(user_session):
    headers = {"Authorization": f"Bearer {user_session.access_token}"}
    response = requests.get(GROUPS_BASE_URI + '/' + MY_GROUPS_PATH,
                            headers=headers)
    response.raise_for_status()
    return response.json()


@app.route('/')
@auth.oidc_auth(AUTH_PROVIDER_NAME)
def login1():
    user_session = UserSession(flask.session)
예제 #11
0
        authorization_endpoint=os.environ.get(
            "GPM_OIDC_AUTHORIZATION_ENDPOINT"),
        jwks_uri=os.environ.get("GPM_OIDC_JWKS_URI"),
        token_endpoint=os.environ.get("GPM_OIDC_TOKEN_ENDPOINT"),
        token_introspection_endpoint=os.environ.get(
            "GPM_OIDC_INTROSPECTION_ENDPOINT"),
        userinfo_endpoint=os.environ.get("GPM_OIDC_USERINFO_ENDPOINT"),
        end_session_endpoint=os.environ.get("GPM_OIDC_END_SESSION_ENDPOINT"),
    )

    provider_config = ProviderConfiguration(
        issuer=os.environ.get("GPM_OIDC_ISSUER"),
        provider_metadata=provider_metadata,
        session_refresh_interval_seconds=10,
        client_metadata=ClientMetadata(
            client_id=os.environ.get("GPM_OIDC_CLIENT_ID"),
            client_secret=os.environ.get("GPM_OIDC_CLIENT_SECRET"),
        ),
    )

    auth = OIDCAuthentication({"oidc": provider_config}, app)
else:
    app.logger.info("RUNNING WITH AUTHENTICATION DISABLED")

# This snippet tries to detect if the app is running on a K8S cluster or locally
try:
    app.logger.info("Attempting init with KUBECONFIG")
    config.load_kube_config()
    app.logger.info(
        f"KUBECONFIG '{config.kube_config.KUBE_CONFIG_DEFAULT_LOCATION}' successfuly loaded."
    )
    app.config["MODE"] = "KUBECONFIG"
예제 #12
0
app.config.from_envvar('COGNITO_DEMO_SETTINGS')
app.config.update({
    'OIDC_REDIRECT_URI': 'http://*****:*****@app.route('/')
@auth.oidc_auth('cognito')
def index():
    user_session = UserSession(flask.session)
예제 #13
0
        authorization_endpoint=os.environ.get(
            'GPM_OIDC_AUTHORIZATION_ENDPOINT'),
        jwks_uri=os.environ.get('GPM_OIDC_JWKS_URI'),
        token_endpoint=os.environ.get('GPM_OIDC_TOKEN_ENDPOINT'),
        token_introspection_endpoint=os.environ.get(
            'GPM_OIDC_INTROSPECTION_ENDPOINT'),
        userinfo_endpoint=os.environ.get('GPM_OIDC_USERINFO_ENDPOINT'),
        end_session_endpoint=os.environ.get('GPM_OIDC_END_SESSION_ENDPOINT'),
    )

    provider_config = ProviderConfiguration(
        issuer=os.environ.get('GPM_OIDC_ISSUER'),
        provider_metadata=provider_metadata,
        session_refresh_interval_seconds=10,
        client_metadata=ClientMetadata(
            client_id=os.environ.get('GPM_OIDC_CLIENT_ID'),
            client_secret=os.environ.get('GPM_OIDC_CLIENT_SECRET')))

    auth = OIDCAuthentication({'oidc': provider_config}, app)
else:
    app.logger.info('RUNNING WITH AUTHENTICATION DISABLED')


def dict_to_li(my_dict, html):
    """Recursive function to convert dict items into <li> html tags"""
    if my_dict is None:
        return html
    for k, v in my_dict.items():
        app.logger.debug("Processing %s, %s" % (k, v))
        if not isinstance(v, dict):
            html += '<li>%s: %s</li>' % (k, v)
예제 #14
0
 def client_metadata(self):
     return ClientMetadata(
         client_id=self.oidc_config.client_id(),
         client_secret=self.oidc_config.client_secret(),
     )
예제 #15
0
# config
logger.info("Choosing config")
if 'prod' in os.environ.get('ENVIRONMENT').lower():
    logger.info("Using production config")
    app.config.from_object(config.ProductionConfig())
else:
    # Only log flask debug in development mode.
    logger.info("Using development config")
    logging.basicConfig(level=logging.DEBUG)
    app.config.from_object(config.DevelopmentConfig())

# setup oidc
oidc_config = config.OIDCConfig()
auth0_Config = ProviderConfiguration(
    issuer='https://{}'.format(oidc_config.OIDC_DOMAIN),
    client_metadata=ClientMetadata(oidc_config.OIDC_CLIENT_ID,
                                   oidc_config.OIDC_CLIENT_SECRET))
oidc = OIDCAuthentication({'auth0': auth0_Config}, app=app)

#websec headers:
headers = {
    'Content-Security-Policy':
    ("default-src 'self'; form-action 'self'; connect-src 'self'; font-src 'self' https://fonts.gstatic.com; img-src 'self'; script-src 'self' ; style-src 'self' https://fonts.googleapis.com/;"
     )
}


@app.route('/')
@add_response_headers(headers=headers)
def main_page():
    return render_template("main_page.html")
예제 #16
0
        'OIDC_REDIRECT_URI': os.getenv('FULL_HOSTNAME') + '/redirect_uri',
        'SECRET_KEY': 'my_not_so_dirty_secret_key',
        'PERMANENT_SESSION_LIFETIME':
        1800,  # session time in second (30 minutes)
        'DEBUG': os.getenv("FLASK_DEBUG", False)
    })

    # General setup based on the obtained configuration
    # Configure database access

    app.config['SQLALCHEMY_DATABASE_URI'] = DB2_URI
    app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
    app.config['SQLALCHEMY_ECHO'] = False

    # Configure access to App ID service for the OpenID Connect client
    appID_clientinfo = ClientMetadata(client_id=APPID_CLIENT_ID,
                                      client_secret=APPID_SECRET)
    appID_config = ProviderConfiguration(issuer=APPID_OAUTH_SERVER_URL,
                                         client_metadata=appID_clientinfo)

    # Initialize OpenID Connect client
    auth = OIDCAuthentication({'default': appID_config}, app)

    # Initialize SQLAlchemy for our database
    db = SQLAlchemy(app, session_options={'autocommit': True})

    # Three (3) decorators that wrap the auth decorators. See the comments
    # in the ELSE for the background
    def security_decorator_auth(f):
        @wraps(f)
        @auth.oidc_auth('default')
        def decorated_function(*args, **kwargs):
예제 #17
0
        'application/json', 'application/javascript', 'application/trig'
    ],
)
app.json_encoder = FixedJSONEncoder
app.url_map.converters['job'] = JobConverter
app.url_map.converters['type'] = TypeConverter

CORS(app)

auth = OIDCAuthentication(
    {
        'default':
        ProviderConfiguration(
            issuer=os.environ['OIDC_SERVER'],
            client_metadata=ClientMetadata(
                client_id=os.environ['OIDC_CLIENT_ID'],
                client_secret=os.environ['OIDC_CLIENT_SECRET']),
            auth_request_params={'scope': ['openid', 'email', 'profile']},
        )
    }, app) if 'OIDC_SERVER' in os.environ and len(
        os.environ['OIDC_SERVER']) > 0 else None

socketio = SocketIO(app, cors_allowed_origins='*')
socketio.start_background_task(emit_database_events)


def authenticated(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        if auth:
            user_session = UserSession(session, 'default')
예제 #18
0
app = Flask(__name__)
csrf = CSRFProtect(app)
csrf.init_app(app)

# Get app config from absolute file path
if os.path.exists(os.path.join(os.getcwd(), "config.py")):
    app.config.from_pyfile(os.path.join(os.getcwd(), "config.py"))
else:
    app.config.from_pyfile(os.path.join(os.getcwd(), "config.env.py"))

db = SQLAlchemy(app)

# OIDC Authentication
CSH_AUTH = ProviderConfiguration(issuer=app.config["OIDC_ISSUER"],
                                 client_metadata=ClientMetadata(
                                     app.config["OIDC_CLIENT_ID"],
                                     app.config["OIDC_CLIENT_SECRET"]))
GOOGLE_AUTH = ProviderConfiguration(issuer=app.config["GOOGLE_ISSUER"],
                                    client_metadata=ClientMetadata(
                                        app.config["GOOGLE_CLIENT_ID"],
                                        app.config["GOOGLE_CLIENT_SECRET"]), 
                                    auth_request_params={'scope': ['email', 'profile', 'openid']})
auth = OIDCAuthentication({'default': CSH_AUTH,
                           'google': GOOGLE_AUTH},
                          app)
auth.init_app(app)

# Flask-Login Manager
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = 'login'
예제 #19
0
    app_config.from_pyfile(os.path.join(os.getcwd(), "config.env.py"))
app.config.update(app_config)

db: SQLAlchemy = SQLAlchemy(app)
migrate = flask_migrate.Migrate(app, db)

# Disable SSL certificate verification warning
requests.packages.urllib3.disable_warnings()

auth = OIDCAuthentication(
    {
        'default':
        ProviderConfiguration(
            issuer=app_config['OIDC_ISSUER'],
            client_metadata=ClientMetadata(
                client_id=app_config['OIDC_CLIENT_ID'],
                client_secret=app_config['OIDC_CLIENT_SECRET']))
    }, app)

if "LDAP_BIND_DN" in app.config:
    ldap = LDAPWrapper(
        CSHLDAP(
            app.config['LDAP_BIND_DN'],
            app.config['LDAP_BIND_PW'],
        ))
else:
    ldap = LDAPWrapper(
        None,
        app.config.get("EBOARD_UIDS", "").split(","),
        app.config.get("RTP_UIDS", "").split(","),
        app.config.get("ORGANIZER_UIDS", "").split(","),
예제 #20
0
import os

app.config.update({
    'OIDC_REDIRECT_URI':
    os.environ.get('REDIRECT_URI'),
    'SECRET_KEY':
    os.environ.get('SECRET'),
    'PERMANENT_SESSION_LIFETIME':
    datetime.timedelta(days=7).total_seconds(),
    'DEBUG':
    True
})

provider_config = ProviderConfiguration(
    issuer=os.environ.get('OAUTH_SERVER_URL'),
    client_metadata=ClientMetadata(os.environ.get('CLIENT_ID'),
                                   os.environ.get('SECRET')))

PROVIDER = 'provider'
auth = OIDCAuthentication({PROVIDER: provider_config}, app)


@app.route('/')
@app.route('/index')
def index():
    return render_template('index.html', title='Home')


@app.route('/login')
@auth.oidc_auth(PROVIDER)
def login():
    user_session = UserSession(flask.session)
예제 #21
0
class TestPyoidcFacade(object):
    PROVIDER_BASEURL = 'https://op.example.com'
    PROVIDER_METADATA = ProviderMetadata(PROVIDER_BASEURL,
                                         PROVIDER_BASEURL + '/auth',
                                         PROVIDER_BASEURL + '/jwks')
    CLIENT_METADATA = ClientMetadata('client1', 'secret1')

    def test_registered_client_metadata_is_forwarded_to_pyoidc(self):
        config = ProviderConfiguration(
            provider_metadata=self.PROVIDER_METADATA,
            client_metadata=self.CLIENT_METADATA)
        facade = PyoidcFacade(config, REDIRECT_URI)
        assert facade._client.registration_response

    def test_no_registered_client_metadata_is_handled(self):
        config = ProviderConfiguration(
            provider_metadata=self.PROVIDER_METADATA,
            client_registration_info=ClientRegistrationInfo())
        facade = PyoidcFacade(config, REDIRECT_URI)
        assert not facade._client.registration_response

    def test_is_registered(self):
        unregistered = ProviderConfiguration(
            provider_metadata=self.PROVIDER_METADATA,
            client_registration_info=ClientRegistrationInfo())
        registered = ProviderConfiguration(
            provider_metadata=self.PROVIDER_METADATA,
            client_metadata=self.CLIENT_METADATA)
        assert PyoidcFacade(unregistered,
                            REDIRECT_URI).is_registered() is False
        assert PyoidcFacade(registered, REDIRECT_URI).is_registered() is True

    @responses.activate
    def test_register(self):
        registration_endpoint = self.PROVIDER_BASEURL + '/register'
        responses.add(responses.POST,
                      registration_endpoint,
                      json=self.CLIENT_METADATA.to_dict())

        provider_metadata = self.PROVIDER_METADATA.copy(
            registration_endpoint=registration_endpoint)
        unregistered = ProviderConfiguration(
            provider_metadata=provider_metadata,
            client_registration_info=ClientRegistrationInfo())
        facade = PyoidcFacade(unregistered, REDIRECT_URI)
        facade.register()
        assert facade.is_registered() is True

    def test_authentication_request(self):
        extra_user_auth_params = {'foo': 'bar', 'abc': 'xyz'}
        config = ProviderConfiguration(
            provider_metadata=self.PROVIDER_METADATA,
            client_metadata=self.CLIENT_METADATA,
            auth_request_params=extra_user_auth_params)

        state = 'test_state'
        nonce = 'test_nonce'

        facade = PyoidcFacade(config, REDIRECT_URI)
        extra_lib_auth_params = {'foo': 'baz', 'qwe': 'rty'}
        auth_request = facade.authentication_request(state, nonce,
                                                     extra_lib_auth_params)
        assert auth_request.startswith(
            self.PROVIDER_METADATA['authorization_endpoint'])

        auth_request_params = dict(parse_qsl(urlparse(auth_request).query))
        expected_auth_params = {
            'scope': 'openid',
            'response_type': 'code',
            'client_id': self.CLIENT_METADATA['client_id'],
            'redirect_uri': REDIRECT_URI,
            'state': state,
            'nonce': nonce
        }
        expected_auth_params.update(extra_user_auth_params)
        expected_auth_params.update(extra_lib_auth_params)
        assert auth_request_params == expected_auth_params

    def test_parse_authentication_response(self):
        facade = PyoidcFacade(
            ProviderConfiguration(provider_metadata=self.PROVIDER_METADATA,
                                  client_metadata=self.CLIENT_METADATA),
            REDIRECT_URI)
        auth_code = 'auth_code-1234'
        state = 'state-1234'
        auth_response = AuthorizationResponse(**{
            'state': state,
            'code': auth_code
        })
        parsed_auth_response = facade.parse_authentication_response(
            auth_response.to_dict())
        assert isinstance(parsed_auth_response, AuthorizationResponse)
        assert parsed_auth_response.to_dict() == auth_response.to_dict()

    def test_parse_authentication_response_handles_error_response(self):
        facade = PyoidcFacade(
            ProviderConfiguration(provider_metadata=self.PROVIDER_METADATA,
                                  client_metadata=self.CLIENT_METADATA),
            REDIRECT_URI)
        error_response = AuthorizationErrorResponse(**{
            'error': 'invalid_request',
            'state': 'state-1234'
        })
        parsed_auth_response = facade.parse_authentication_response(
            error_response)
        assert isinstance(parsed_auth_response, AuthorizationErrorResponse)
        assert parsed_auth_response.to_dict() == error_response.to_dict()

    @responses.activate
    def test_parse_authentication_response_preserves_id_token_jwt(self):
        facade = PyoidcFacade(
            ProviderConfiguration(provider_metadata=self.PROVIDER_METADATA,
                                  client_metadata=self.CLIENT_METADATA),
            REDIRECT_URI)
        state = 'state-1234'
        now = int(time.time())
        id_token, id_token_signing_key = signed_id_token({
            'iss':
            self.PROVIDER_METADATA['issuer'],
            'sub':
            'test_sub',
            'aud':
            'client1',
            'exp':
            now + 1,
            'iat':
            now
        })
        responses.add(responses.GET,
                      self.PROVIDER_METADATA['jwks_uri'],
                      json={'keys': [id_token_signing_key.serialize()]})
        auth_response = AuthorizationResponse(**{
            'state': state,
            'id_token': id_token
        })
        parsed_auth_response = facade.parse_authentication_response(
            auth_response)
        assert isinstance(parsed_auth_response, AuthorizationResponse)
        assert parsed_auth_response['state'] == state
        assert parsed_auth_response['id_token_jwt'] == id_token

    @pytest.mark.parametrize(
        'request_func,expected_token_request',
        [(lambda facade: facade.exchange_authorization_code('auth-code'), {
            'grant_type': 'authorization_code',
            'code': 'auth-code',
            'redirect_uri': REDIRECT_URI
        }),
         (lambda facade: facade.refresh_token('refresh-token'), {
             'grant_type': 'refresh_token',
             'refresh_token': 'refresh-token',
             'redirect_uri': REDIRECT_URI
         })])
    @responses.activate
    def test_token_request(self, request_func, expected_token_request):
        token_endpoint = self.PROVIDER_BASEURL + '/token'
        now = int(time.time())
        id_token_claims = {
            'iss': self.PROVIDER_METADATA['issuer'],
            'sub': 'test_user',
            'aud': [self.CLIENT_METADATA['client_id']],
            'exp': now + 1,
            'iat': now,
            'nonce': 'test_nonce'
        }
        id_token_jwt, id_token_signing_key = signed_id_token(id_token_claims)
        token_response = AccessTokenResponse(access_token='test_access_token',
                                             token_type='Bearer',
                                             id_token=id_token_jwt)
        responses.add(responses.POST,
                      token_endpoint,
                      json=token_response.to_dict())

        provider_metadata = self.PROVIDER_METADATA.copy(
            token_endpoint=token_endpoint)
        facade = PyoidcFacade(
            ProviderConfiguration(provider_metadata=provider_metadata,
                                  client_metadata=self.CLIENT_METADATA),
            REDIRECT_URI)

        responses.add(responses.GET,
                      self.PROVIDER_METADATA['jwks_uri'],
                      json={'keys': [id_token_signing_key.serialize()]})
        token_response = request_func(facade)

        assert isinstance(token_response, AccessTokenResponse)
        expected_token_response = token_response.to_dict()
        expected_token_response['id_token'] = id_token_claims
        expected_token_response['id_token_jwt'] = id_token_jwt
        assert token_response.to_dict() == expected_token_response

        token_request = dict(parse_qsl(responses.calls[0].request.body))
        assert token_request == expected_token_request

    @responses.activate
    def test_token_request_handles_error_response(self):
        token_endpoint = self.PROVIDER_BASEURL + '/token'
        token_response = TokenErrorResponse(
            error='invalid_request',
            error_description='test error description')
        responses.add(responses.POST,
                      token_endpoint,
                      json=token_response.to_dict(),
                      status=400)

        provider_metadata = self.PROVIDER_METADATA.copy(
            token_endpoint=token_endpoint)
        facade = PyoidcFacade(
            ProviderConfiguration(provider_metadata=provider_metadata,
                                  client_metadata=self.CLIENT_METADATA),
            REDIRECT_URI)
        assert facade.exchange_authorization_code('1234') == token_response

    def test_token_request_handles_missing_provider_token_endpoint(self):
        facade = PyoidcFacade(
            ProviderConfiguration(provider_metadata=self.PROVIDER_METADATA,
                                  client_metadata=self.CLIENT_METADATA),
            REDIRECT_URI)
        assert facade.exchange_authorization_code('1234') is None

    @pytest.mark.parametrize('userinfo_http_method', ['GET', 'POST'])
    @responses.activate
    def test_configurable_userinfo_endpoint_method_is_used(
            self, userinfo_http_method):
        userinfo_endpoint = self.PROVIDER_BASEURL + '/userinfo'
        userinfo_response = OpenIDSchema(sub='user1')
        responses.add(userinfo_http_method,
                      userinfo_endpoint,
                      json=userinfo_response.to_dict())

        provider_metadata = self.PROVIDER_METADATA.copy(
            userinfo_endpoint=userinfo_endpoint)
        facade = PyoidcFacade(
            ProviderConfiguration(provider_metadata=provider_metadata,
                                  client_metadata=self.CLIENT_METADATA,
                                  userinfo_http_method=userinfo_http_method),
            REDIRECT_URI)
        assert facade.userinfo_request('test_token') == userinfo_response

    def test_no_userinfo_request_is_made_if_no_userinfo_http_method_is_configured(
            self):
        facade = PyoidcFacade(
            ProviderConfiguration(provider_metadata=self.PROVIDER_METADATA,
                                  client_metadata=self.CLIENT_METADATA,
                                  userinfo_http_method=None), REDIRECT_URI)
        assert facade.userinfo_request('test_token') is None

    def test_no_userinfo_request_is_made_if_no_userinfo_endpoint_is_configured(
            self):
        facade = PyoidcFacade(
            ProviderConfiguration(provider_metadata=self.PROVIDER_METADATA,
                                  client_metadata=self.CLIENT_METADATA),
            REDIRECT_URI)
        assert facade.userinfo_request('test_token') is None

    def test_no_userinfo_request_is_made_if_no_access_token(self):
        provider_metadata = self.PROVIDER_METADATA.copy(
            userinfo_endpoint=self.PROVIDER_BASEURL + '/userinfo')
        facade = PyoidcFacade(
            ProviderConfiguration(provider_metadata=provider_metadata,
                                  client_metadata=self.CLIENT_METADATA),
            REDIRECT_URI)
        assert facade.userinfo_request(None) is None
예제 #22
0
from flask_pyoidc.provider_configuration import ClientMetadata, ProviderConfiguration
from flask_pyoidc.user_session import UserSession
from redis import RedisError

from api.my_flask import app
from utility.create_config import create_config

config = create_config()
client_id = config.get('Secrets-Section', 'client-id')
client_secret = config.get('Secrets-Section', 'client-secret')
redirect_uris = config.get('Web-Section', 'redirect-oidc').split()
issuer = config.get('Web-Section', 'issuer')
my_uri = config.get('Web-Section', 'my-uri')

client_metadata = ClientMetadata(client_id=client_id,
                                 client_secret=client_secret,
                                 redirect_uris=redirect_uris)
provider_config = ProviderConfiguration(issuer=issuer,
                                        client_metadata=client_metadata)
ietf_auth = OIDCAuthentication({'default': provider_config})


class YangCatalogAdminBlueprint(Blueprint):
    def __init__(self,
                 name,
                 import_name,
                 static_folder=None,
                 static_url_path=None,
                 template_folder=None,
                 url_prefix=None,
                 subdomain=None,
예제 #23
0
    'localhost:5000',
    'SECRET_KEY':
    'dev_key',  # make sure to change this!!
    'PERMANENT_SESSION_LIFETIME':
    datetime.timedelta(days=7).total_seconds(),
    'PREFERRED_URL_SCHEME':
    'http',
    'DEBUG':
    True
})

ISSUER1 = 'https://provider1.example.com'
CLIENT1 = 'client@provider1'
PROVIDER_NAME1 = 'provider1'
PROVIDER_CONFIG1 = ProviderConfiguration(issuer=ISSUER1,
                                         client_metadata=ClientMetadata(
                                             CLIENT1, 'secret1'))
ISSUER2 = 'https://provider2.example.com'
CLIENT2 = 'client@provider2'
PROVIDER_NAME2 = 'provider2'
PROVIDER_CONFIG2 = ProviderConfiguration(issuer=ISSUER2,
                                         client_metadata=ClientMetadata(
                                             CLIENT2, 'secret2'))
auth = OIDCAuthentication({
    PROVIDER_NAME1: PROVIDER_CONFIG1,
    PROVIDER_NAME2: PROVIDER_CONFIG2
})


@app.route('/')
@auth.oidc_auth(PROVIDER_NAME1)
def login1():
예제 #24
0
    openid_config['scope'] = ['openid', 'email', 'profile']

app = Flask(__name__)
app.config.update(
    SECRET_KEY=''.join(random.SystemRandom().choice(string.ascii_uppercase +
                                                    string.digits)
                       for _ in range(16)),
    SERVER_NAME=f'{socket.getfqdn()}:5000',
    JSONIFY_PRETTYPRINT_REGULAR=True,
)

provider_config = ProviderConfiguration(
    auth_request_params={'scope': openid_config['scope']},
    issuer=openid_config['issuer'],
    client_metadata=ClientMetadata(
        client_id=openid_config['client_id'],
        client_secret=openid_config['client_secret'],
    ))

auth = OIDCAuthentication({'default': provider_config}, app)


@app.route('/')
@auth.oidc_auth('default')
def login():
    user_session = UserSession(flask.session, provider_name='default').userinfo
    print(user_session)
    return jsonify(user_session)


@app.route('/logout')
@auth.oidc_logout
예제 #25
0
# Fetch the version number from the npm package file
with open(os.path.join(_root_dir, 'package.json')) as package_file:
    app.config['VERSION'] = json.load(package_file)['version']

# Logger configuration
logging.getLogger().setLevel(app.config['LOG_LEVEL'])
app.logger.info('Launching packet v' + app.config['VERSION'])
app.logger.info('Using the {} realm'.format(app.config['REALM']))

# Initialize the extensions
db = SQLAlchemy(app)
migrate = Migrate(app, db)
app.logger.info('SQLAlchemy pointed at ' + repr(db.engine.url))

APP_CONFIG = ProviderConfiguration(issuer=app.config['OIDC_ISSUER'],
                          client_metadata=ClientMetadata(app.config['OIDC_CLIENT_ID'],
                                                            app.config['OIDC_CLIENT_SECRET']))

# Initialize Onesignal Notification apps
csh_onesignal_client = onesignal.Client(user_auth_key=app.config['ONESIGNAL_USER_AUTH_KEY'],
                                    app_auth_key=app.config['ONESIGNAL_CSH_APP_AUTH_KEY'],
                                    app_id=app.config['ONESIGNAL_CSH_APP_ID'])

intro_onesignal_client = onesignal.Client(user_auth_key=app.config['ONESIGNAL_USER_AUTH_KEY'],
                                    app_auth_key=app.config['ONESIGNAL_INTRO_APP_AUTH_KEY'],
                                    app_id=app.config['ONESIGNAL_INTRO_APP_ID'])

# OIDC Auth
auth = OIDCAuthentication({'app': APP_CONFIG}, app)

# LDAP
_ldap = csh_ldap.CSHLDAP(app.config['LDAP_BIND_DN'], app.config['LDAP_BIND_PASS'])
예제 #26
0
app.config.from_pyfile('settings.py')

issuer = app.config['OIDC_ISSUER']
client = app.config['OIDC_CLIENT']
secret = app.config['OIDC_SECRET']
auth_params = app.config['OIDC_SCOPES']
second_factor = {
    'id_token': {
        'acr': {
            'essential': True,
            'value': 'https://refeds.org/profile/mfa'
        }
    }
}

client_metadata = ClientMetadata(client_id=client, client_secret=secret)
config = ProviderConfiguration(issuer=issuer,
                               client_metadata=client_metadata,
                               auth_request_params=auth_params)
auth = OIDCAuthentication({'default': config}, app)


# Used for returning the authn response to the browser
def get_user_data():
    if flask.session:
        user = UserSession(flask.session)
        app.logger.info(f'{user.id_token["sub"]} logged in')

        data = {
            'access_token': user.access_token,
            'id_token': user.id_token,
예제 #27
0
class TestPyoidcFacade(object):
    PROVIDER_BASEURL = "http://rp.example.com"
    PROVIDER_METADATA = ProviderMetadata(PROVIDER_BASEURL,
                                         PROVIDER_BASEURL + "/auth",
                                         PROVIDER_BASEURL + "/jwks")
    CLIENT_METADATA = ClientMetadata("client1", "secret1")
    CLIENT_DOMAIN = "client.example.com"
    REDIRECT_URI = "redirect_uri"
    FULL_REDIRECT_URI = "http://client.example.com/redirect_uri"

    @pytest.fixture(autouse=True)
    def create_flask_app(self):
        self.app = Flask(__name__)
        self.app.add_url_rule("/redirect_uri", "redirect_uri")
        self.app.config.update({
            "SERVER_NAME": self.CLIENT_DOMAIN,
            "SECRET_KEY": "test_key"
        })

    def test_registered_client_metadata_is_forwarded_to_pyoidc(self):
        config = ProviderConfiguration(
            provider_metadata=self.PROVIDER_METADATA,
            client_metadata=self.CLIENT_METADATA,
        )
        facade = PyoidcFacade(config, self.REDIRECT_URI)
        assert facade._client.registration_response

    def test_no_registered_client_metadata_is_handled(self):
        config = ProviderConfiguration(
            provider_metadata=self.PROVIDER_METADATA,
            client_registration_info=ClientRegistrationInfo(),
        )
        facade = PyoidcFacade(config, self.REDIRECT_URI)
        assert not facade._client.registration_response

    def test_is_registered(self):
        unregistered = ProviderConfiguration(
            provider_metadata=self.PROVIDER_METADATA,
            client_registration_info=ClientRegistrationInfo(),
        )
        registered = ProviderConfiguration(
            provider_metadata=self.PROVIDER_METADATA,
            client_metadata=self.CLIENT_METADATA,
        )
        assert PyoidcFacade(unregistered,
                            self.REDIRECT_URI).is_registered() is False
        assert PyoidcFacade(registered,
                            self.REDIRECT_URI).is_registered() is True

    @responses.activate
    def test_register(self):
        registration_endpoint = self.PROVIDER_BASEURL + "/register"
        responses.add(responses.POST,
                      registration_endpoint,
                      json=self.CLIENT_METADATA.to_dict())

        provider_metadata = self.PROVIDER_METADATA.copy(
            registration_endpoint=registration_endpoint)
        unregistered = ProviderConfiguration(
            provider_metadata=provider_metadata,
            client_registration_info=ClientRegistrationInfo(),
        )
        facade = PyoidcFacade(unregistered, self.REDIRECT_URI)
        facade.register()
        assert facade.is_registered() is True

    def test_authentication_request(self):
        extra_user_auth_params = {"foo": "bar", "abc": "xyz"}
        config = ProviderConfiguration(
            provider_metadata=self.PROVIDER_METADATA,
            client_metadata=self.CLIENT_METADATA,
            auth_request_params=extra_user_auth_params,
        )

        state = "test_state"
        nonce = "test_nonce"

        facade = PyoidcFacade(config, self.REDIRECT_URI)
        extra_lib_auth_params = {"foo": "baz", "qwe": "rty"}
        auth_request = facade.authentication_request(state, nonce,
                                                     self.FULL_REDIRECT_URI,
                                                     extra_lib_auth_params)
        assert auth_request.startswith(
            self.PROVIDER_METADATA["authorization_endpoint"])

        auth_request_params = dict(parse_qsl(urlparse(auth_request).query))
        expected_auth_params = {
            "scope": "openid",
            "response_type": "code",
            "client_id": self.CLIENT_METADATA["client_id"],
            "redirect_uri": self.FULL_REDIRECT_URI,
            "state": state,
            "nonce": nonce,
        }
        expected_auth_params.update(extra_user_auth_params)
        expected_auth_params.update(extra_lib_auth_params)
        assert auth_request_params == expected_auth_params

    def test_parse_authentication_response(self):
        facade = PyoidcFacade(
            ProviderConfiguration(
                provider_metadata=self.PROVIDER_METADATA,
                client_metadata=self.CLIENT_METADATA,
            ),
            self.REDIRECT_URI,
        )
        auth_code = "auth_code-1234"
        state = "state-1234"
        auth_response = AuthorizationResponse(**{
            "state": state,
            "code": auth_code
        })
        parsed_auth_response = facade.parse_authentication_response(
            auth_response.to_dict())
        assert isinstance(parsed_auth_response, AuthorizationResponse)
        assert parsed_auth_response.to_dict() == auth_response.to_dict()

    def test_parse_authentication_response_handles_error_response(self):
        facade = PyoidcFacade(
            ProviderConfiguration(
                provider_metadata=self.PROVIDER_METADATA,
                client_metadata=self.CLIENT_METADATA,
            ),
            self.REDIRECT_URI,
        )
        error_response = AuthorizationErrorResponse(**{
            "error": "invalid_request",
            "state": "state-1234"
        })
        parsed_auth_response = facade.parse_authentication_response(
            error_response)
        assert isinstance(parsed_auth_response, AuthorizationErrorResponse)
        assert parsed_auth_response.to_dict() == error_response.to_dict()

    @responses.activate
    def test_parse_authentication_response_preserves_id_token_jwt(self):
        facade = PyoidcFacade(
            ProviderConfiguration(
                provider_metadata=self.PROVIDER_METADATA,
                client_metadata=self.CLIENT_METADATA,
            ),
            self.REDIRECT_URI,
        )
        state = "state-1234"
        now = int(time.time())
        id_token, id_token_signing_key = signed_id_token({
            "iss":
            self.PROVIDER_METADATA["issuer"],
            "sub":
            "test_sub",
            "aud":
            "client1",
            "exp":
            now + 1,
            "iat":
            now,
        })
        responses.add(
            responses.GET,
            self.PROVIDER_METADATA["jwks_uri"],
            json={"keys": [id_token_signing_key.serialize()]},
        )
        auth_response = AuthorizationResponse(**{
            "state": state,
            "id_token": id_token
        })
        parsed_auth_response = facade.parse_authentication_response(
            auth_response)
        assert isinstance(parsed_auth_response, AuthorizationResponse)
        assert parsed_auth_response["state"] == state
        assert parsed_auth_response["id_token_jwt"] == id_token

    @responses.activate
    def test_token_request(self):
        token_endpoint = self.PROVIDER_BASEURL + "/token"
        now = int(time.time())
        id_token_claims = {
            "iss": self.PROVIDER_METADATA["issuer"],
            "sub": "test_user",
            "aud": [self.CLIENT_METADATA["client_id"]],
            "exp": now + 1,
            "iat": now,
            "nonce": "test_nonce",
        }
        id_token_jwt, id_token_signing_key = signed_id_token(id_token_claims)
        token_response = AccessTokenResponse(access_token="test_access_token",
                                             token_type="Bearer",
                                             id_token=id_token_jwt)
        responses.add(responses.POST,
                      token_endpoint,
                      json=token_response.to_dict())

        provider_metadata = self.PROVIDER_METADATA.copy(
            token_endpoint=token_endpoint)
        facade = PyoidcFacade(
            ProviderConfiguration(
                provider_metadata=provider_metadata,
                client_metadata=self.CLIENT_METADATA,
            ),
            self.REDIRECT_URI,
        )

        auth_code = "auth_code-1234"
        responses.add(
            responses.GET,
            self.PROVIDER_METADATA["jwks_uri"],
            json={"keys": [id_token_signing_key.serialize()]},
        )
        with self.app.app_context():
            token_response = facade.token_request(auth_code)

        assert isinstance(token_response, AccessTokenResponse)
        expected_token_response = token_response.to_dict()
        expected_token_response["id_token"] = id_token_claims
        expected_token_response["id_token_jwt"] = id_token_jwt
        assert token_response.to_dict() == expected_token_response

        token_request = dict(parse_qsl(responses.calls[0].request.body))
        expected_token_request = {
            "grant_type": "authorization_code",
            "code": auth_code,
            "redirect_uri": self.FULL_REDIRECT_URI
        }
        assert token_request == expected_token_request

    @responses.activate
    def test_token_request_handles_error_response(self):
        token_endpoint = self.PROVIDER_BASEURL + "/token"
        token_response = TokenErrorResponse(
            error="invalid_request",
            error_description="test error description")
        responses.add(responses.POST,
                      token_endpoint,
                      json=token_response.to_dict(),
                      status=400)

        provider_metadata = self.PROVIDER_METADATA.copy(
            token_endpoint=token_endpoint)
        facade = PyoidcFacade(
            ProviderConfiguration(
                provider_metadata=provider_metadata,
                client_metadata=self.CLIENT_METADATA,
            ),
            self.REDIRECT_URI,
        )

        with self.app.app_context():
            assert facade.token_request("1234") == token_response

    def test_token_request_handles_missing_provider_token_endpoint(self):
        facade = PyoidcFacade(
            ProviderConfiguration(
                provider_metadata=self.PROVIDER_METADATA,
                client_metadata=self.CLIENT_METADATA,
            ),
            self.REDIRECT_URI,
        )
        assert facade.token_request("1234") is None

    @pytest.mark.parametrize("userinfo_http_method", ["GET", "POST"])
    @responses.activate
    def test_configurable_userinfo_endpoint_method_is_used(
            self, userinfo_http_method):
        userinfo_endpoint = self.PROVIDER_BASEURL + "/userinfo"
        userinfo_response = OpenIDSchema(sub="user1")
        responses.add(userinfo_http_method,
                      userinfo_endpoint,
                      json=userinfo_response.to_dict())

        provider_metadata = self.PROVIDER_METADATA.copy(
            userinfo_endpoint=userinfo_endpoint)
        facade = PyoidcFacade(
            ProviderConfiguration(
                provider_metadata=provider_metadata,
                client_metadata=self.CLIENT_METADATA,
                userinfo_http_method=userinfo_http_method,
            ),
            self.REDIRECT_URI,
        )
        assert facade.userinfo_request("test_token") == userinfo_response

    def test_no_userinfo_request_is_made_if_no_userinfo_http_method_is_configured(
            self):
        facade = PyoidcFacade(
            ProviderConfiguration(
                provider_metadata=self.PROVIDER_METADATA,
                client_metadata=self.CLIENT_METADATA,
                userinfo_http_method=None,
            ),
            self.REDIRECT_URI,
        )
        assert facade.userinfo_request("test_token") is None

    def test_no_userinfo_request_is_made_if_no_userinfo_endpoint_is_configured(
            self):
        facade = PyoidcFacade(
            ProviderConfiguration(
                provider_metadata=self.PROVIDER_METADATA,
                client_metadata=self.CLIENT_METADATA,
            ),
            self.REDIRECT_URI,
        )
        assert facade.userinfo_request("test_token") is None

    def test_no_userinfo_request_is_made_if_no_access_token(self):
        provider_metadata = self.PROVIDER_METADATA.copy(
            userinfo_endpoint=self.PROVIDER_BASEURL + "/userinfo")
        facade = PyoidcFacade(
            ProviderConfiguration(
                provider_metadata=provider_metadata,
                client_metadata=self.CLIENT_METADATA,
            ),
            self.REDIRECT_URI,
        )
        assert facade.userinfo_request(None) is None
예제 #28
0
                      'DEBUG': True})


# General setup based on the obtained configuration
# Configure database access
if dbInfo['port']==50001:
    # if we are on the SSL port, add additional parameter for the driver
    app.config['SQLALCHEMY_DATABASE_URI']=dbInfo['uri']+"Security=SSL;"
else:
    app.config['SQLALCHEMY_DATABASE_URI']=dbInfo['uri']

app.config['SQLALCHEMY_TRACK_MODIFICATIONS']=False
app.config['SQLALCHEMY_ECHO']=False

# Configure access to App ID service for the OpenID Connect client
appID_clientinfo=ClientMetadata(client_id=appIDInfo['clientId'],client_secret=appIDInfo['secret'])
appID_config = ProviderConfiguration(issuer=appIDInfo['oauthServerUrl'],client_metadata=appID_clientinfo)

# Initialize OpenID Connect client
auth=OIDCAuthentication({'default': appID_config}, app)
# Initialize BasicAuth, needed for token access to data
basicauth = HTTPBasicAuth()

# Initialize SQLAlchemy for our database
db = SQLAlchemy(app, session_options={'autocommit': True})

# Encoder to handle some raw data correctly
def alchemyencoder(obj):
    """JSON encoder function for SQLAlchemy special classes."""
    if isinstance(obj, datetime.date):
        return obj.isoformat()
예제 #29
0
def create_app(config=None):
    app = Flask(__name__, instance_relative_config=True)

    # Load application config from various sources
    # ------------------------------------------------------------------------------
    # 1. Defaults from this package
    app.config.from_object(DefaultConfig)

    # 2. From a config.py file in the application directory
    app.config.from_pyfile(filename=os.path.join(os.getcwd(), "config.py"),
                           silent=True)

    # 3. From a dynamically configurable file location
    if os.environ.get('CONFIG_LOCATION'):
        app.config.from_pyfile(filename=os.environ.get('CONFIG_LOCATION'),
                               silent=False)

    # 4. Testing config
    if config:
        app.config.from_mapping(config)

    # 5. Load some final computed config
    #    NOTE: This is placed here as it relies on other config values that
    #          may be configured after the user provided config for example.
    oidc_logout_redirect_uri = os.environ.get(
        'OIDC_LOGOUT_REDIRECT_URI',
        'https://' + app.config['SERVER_NAME'] + '/logout')
    oidc_auth_request_params = json.loads(
        os.environ.get('OIDC_AUTH_REQUEST_PARAMS', '{}'))
    if oidc_auth_request_params:
        if app.config['OIDC_EXTRA_AUTH_REQUEST_PARAMS']:
            app.logger.warning(
                'OIDC_EXTRA_AUTH_REQUEST_PARAMS is being overridden by OIDC_AUTH_REQUEST_PARAMS being explicitly set.'
            )
    else:
        oidc_auth_request_params['scope'] = " ".join(
            re.split(",| ", app.config['OIDC_SCOPE']))
        if app.config['OIDC_EXTRA_AUTH_REQUEST_PARAMS']:
            oidc_auth_request_params.update(
                app.config['OIDC_EXTRA_AUTH_REQUEST_PARAMS'])

    app.config.from_mapping({
        'OIDC_LOGOUT_REDIRECT_URI':
        oidc_logout_redirect_uri,
        'OIDC_CLIENT_METADATA': {
            'client_id': app.config['OIDC_CLIENT_ID'],
            'client_secret': app.config['OIDC_CLIENT_SECRET'],
            'post_logout_redirect_uris': str.split(oidc_logout_redirect_uri,
                                                   ",")
        },
        'OIDC_AUTH_REQUEST_PARAMS':
        oidc_auth_request_params,
    })

    # Initialize OpenID Connect extension
    # ------------------------------------------------------------------------------

    # The client metadata will be consumed no matter what...
    # https://github.com/zamzterz/Flask-pyoidc#dynamic-provider-configuration
    client_metadata = ClientMetadata(**app.config['OIDC_CLIENT_METADATA'])

    # ... but if explicit OIDC provider information is provided, we use that
    # instead of the information dynamically provided by the
    # .well-known/openid-configuration endpoint.
    if app.config['OIDC_PROVIDER_METADATA']:
        provider_metadata = ProviderMetadata(
            **app.config['OIDC_PROVIDER_METADATA'])
        provider = ProviderConfiguration(
            provider_metadata=provider_metadata,
            client_metadata=client_metadata,
            auth_request_params=app.config['OIDC_AUTH_REQUEST_PARAMS'],
        )
    else:
        provider = ProviderConfiguration(
            issuer=app.config['OIDC_ISSUER'],
            client_metadata=client_metadata,
            auth_request_params=app.config['OIDC_AUTH_REQUEST_PARAMS'],
        )

    auth = OIDCAuthentication(
        provider_configurations={
            'default': provider,
        },
        app=app,
    )

    # The /health endpoint returns a JSON string like...
    # {"hostname": "a3731af16461", "status": "success", "timestamp": 1551186453.8854501, "results": []}
    health = HealthCheck(app, "/health")

    # If an OAuth error response is received, either in the authentication or
    # token response, it will be passed to the "error view".
    @auth.error_view
    def error(error=None, error_description=None):
        return jsonify({'error': error, 'message': error_description})

    @app.route('/')
    def index():
        """
        If a user tries to access this application directly,
        just redirect them to Discourse.
        :return: Redirect to the configurated DISCOURSE_URL
        """
        return redirect(app.config.get('DISCOURSE_URL'), 302)

    @app.route('/sso/login')
    def payload_check():
        """
        Verify the payload and signature coming from a Discourse server and if
        correct redirect to the authentication page after saving the nonce in
        the session as discourse_nonce.
        
        :return: The redirection page to the authentication page
        """

        # Get payload and signature from Discourse request
        payload = request.args.get('sso', '')
        signature = request.args.get('sig', '')

        if not payload or not signature:
            app.logger.info(
                '/sso/login -> 400: missing payload="%s" or signature="%s"',
                payload, signature)
            abort(400)

        app.logger.debug('Request to login with payload="%s" signature="%s"',
                         payload, signature)
        app.logger.debug('Session Secret Key: %s', app.secret_key)
        app.logger.debug('SSO Secret Key: %s',
                         app.config.get('DISCOURSE_SECRET_KEY'))

        # Calculate and compare request signature
        dig = hmac.new(
            app.config.get('DISCOURSE_SECRET_KEY', '').encode('utf-8'),
            payload.encode('utf-8'), hashlib.sha256).hexdigest()
        app.logger.debug('Calculated hash: %s', dig)

        if dig != signature:
            app.logger.info(
                '/sso/login -> 400: dig / signature mismatch. dig="%s" and signature="%s"',
                dig, signature)
            abort(400)

        # Decode the payload and store in session
        decoded_msg = base64.b64decode(payload).decode('utf-8')
        session[
            'discourse_nonce'] = decoded_msg  # This can't just be 'nonce' as Flask-pyoidc will steamroll it

        # Redirect to authorization endpoint
        return redirect(url_for('sso_auth'))

    @app.route('/sso/auth')
    @auth.oidc_auth('default')
    def sso_auth():
        """
        Read the user attributes provided by Flask-pyoidc and
        create the payload to send to Discourse.
        :return: The redirection page to Discourse
        """

        # Check to make sure we have a valid session
        if 'discourse_nonce' not in session:
            app.logger.info(
                '/sso/auth -> 403: discourse_nonce not found in session, arriving here without coming from /sso/login?'
            )
            abort(403)

        attribute_map = app.config.get('USERINFO_SSO_MAP')

        sso_attributes = {}
        userinfo = session['userinfo']

        # Check if the provided userinfo should be used to set information to be
        # passed to discourse. Do it by checking if the userinfo field is...
        # 1. explicitly mapped using the provided map
        # 2. if it can match one of the known attributes with discourse_ prefixed
        # 3. if it can match one of the known attributes directly
        for userinfo_key, userinfo_value in userinfo.items():
            attribute_key = attribute_map.get(userinfo_key)

            if attribute_key:
                pass
            elif userinfo_key in [
                    "discourse_" + attr for attr in ALL_ATTRIBUTES
            ]:
                attribute_key = userinfo_key[len("discourse_"):]
            elif userinfo_key in ALL_ATTRIBUTES:
                attribute_key = userinfo_key

            if attribute_key:
                if attribute_key in BOOL_ATTRIBUTES:
                    userinfo_value = "false" if str.lower(
                        str(userinfo_value)) in ['false', 'f', '0'] else "true"
                sso_attributes[attribute_key] = userinfo_value

        # Check if we have a default value that should be used
        default_sso_attributes = app.config.get('DEFAULT_SSO_ATTRIBUTES')
        for default_attribute_key, default_attribute_value in default_sso_attributes.items(
        ):
            if default_attribute_key not in sso_attributes:
                sso_attributes[default_attribute_key] = default_attribute_value

        # Check if we got the required attributes
        for required_attribute in REQUIRED_ATTRIBUTES:
            if not sso_attributes.get(required_attribute):
                app.logger.info(
                    f'/sso/auth -> 403: {required_attribute} not found in userinfo: '
                    + json.dumps(session['userinfo']))
                abort(403)

        # All systems are go!
        app.logger.debug(
            f'Authenticating "{sso_attributes.get("external_id")}", named "{sso_attributes.get("name")}" with email: "{sso_attributes.get("email")}"'
        )

        # Construct the response inner query parameters
        query = session['discourse_nonce']
        for sso_attribute_key, sso_attribute_value in sso_attributes.items():
            query += f'&{sso_attribute_key}={quote(str(sso_attribute_value))}'
        app.logger.debug('Query string to return: %s', query)

        # Encode response
        query_b64 = base64.b64encode(query.encode('utf-8'))
        app.logger.debug('Base64 query string to return: %s', query_b64)

        # Build URL-safe response
        query_urlenc = quote(query_b64)
        app.logger.debug('URLEnc query string to return: %s', query_urlenc)

        # Generate signature for response
        sig = hmac.new(
            app.config.get('DISCOURSE_SECRET_KEY').encode('utf-8'),
            query_b64,
            hashlib.sha256,
        ).hexdigest()

        app.logger.debug('Signature: %s', sig)

        # Build redirect URL
        redirect_url = (app.config.get('DISCOURSE_URL') + '/session/sso_login?'
                        'sso=' + query_urlenc + '&sig=' + sig)

        # Redirect back to Discourse
        return redirect(redirect_url)

    @app.route('/logout')
    @auth.oidc_logout
    def logout():
        """
        Handle logging a user out. Flask-pyoidc does the heavy lifting here.
        :return: Redirect to the application index
        """
        return redirect(url_for('index'), 302)

    @app.errorhandler(403)
    def attribute_not_provided(error):
        """
        Render a custom error page in case the IdP authenticate the user but does
        not provide the requested attributes
        :type error: object
        """
        app.logger.info(f'403: error: "{error}"')
        return render_template('403.html'), 403

    return app
예제 #30
0
파일: app_rp2.py 프로젝트: tom333/flask-op
from flask import Flask, jsonify

from flask_pyoidc import OIDCAuthentication
from flask_pyoidc.provider_configuration import ProviderConfiguration, ClientMetadata
from flask_pyoidc.user_session import UserSession


app = Flask(__name__)
# See http://flask.pocoo.org/docs/0.12/config/
app.config.update({"OIDC_REDIRECT_URI": "http://*****:*****@app.route("/")
@auth.oidc_auth(PROVIDER_NAME)
def login():
    user_session = UserSession(flask.session)
    return jsonify(access_token=user_session.access_token, id_token=user_session.id_token, userinfo=user_session.userinfo)


@app.route("/logout")