2015-01-13 2 views
11

저는 SAML 2.0 기반 서비스 공급자를 Python으로 구현하려고합니다.Python의 SAML 2.0 서비스 공급자

내 웹 응용 프로그램은 현재 모두 Flask 응용 프로그램입니다. 필자는 Flask 청사진/데코레이터를 만들어 기존 로그인 응용 프로그램에 싱글 사인온 기능을 추가 할 계획입니다.

내가 광범위하게 python-saml을 들여다 보았습니다. 불행히도 이미 존재하는 서버/앱이 너무 많아서 해결할 가치가없는 종속성 문제가 있습니다. 호환되지 않는 환경이 있습니다.

PySAML2이 작동하는 것처럼 보이지만 문서가 거의 없으며 어떤 문서를 사용할 수 있는지 이해하는데 문제가 있습니다. Flask 앱에는 PySAML2의 예제가 없습니다.

내가 가진 ID 공급자는 Okta입니다. Okta에서 로그인 한 후 내 앱으로 리디렉션되도록 Okta를 설정했습니다.

누구든지 PySAML2 사용에 대한 조언을 제공하거나 내 애플리케이션을 방문하는 SAML 2.0을 사용하여 사용자를 가장 잘 인증하는 방법에 대한 조언을 제공 할 수 있습니까?

답변

14

업데이트 : using PySAML2 with Okta에 대한 자세한 설명은 developer.okta.com에 있습니다.

다음은 Python/Flask에서 SAML SP를 구현하기위한 몇 가지 샘플 코드입니다. 이 샘플 코드는 몇 가지를 보여줍니다.

  1. 여러 IdP 지원.
  2. 사용자 관리에 Flask-Login을 사용합니다.
  3. 잠재 고객 제한으로 "SSO URL"사용 (IdP에서 구성을 단순화하기 위해).
  4. 사용자 프로비저닝 ("SAML JIT")
  5. 애트리뷰트 문에 추가 사용자 정보를 전달합니다.

무엇 SP 초기화 인증 요청을하고있다 증명하지- 나중에 그와 함께 후속 수 있습니다.

어느 시점에서, 나는 기본값을 가진 pysaml2 주위에 래퍼를 만들려고합니다.

마지막으로 python-saml과 마찬가지로 pysaml2 라이브러리는 xmlsec1 바이너리를 사용합니다. 이로 인해 서버 환경에서 종속성 문제가 발생할 수도 있습니다. 그렇다면 xmlsec1signxml 라이브러리로 대체하는 것이 좋습니다. 아래의 샘플

모두는 다음 설치 작업을해야합니다

$ virtualenv venv 
$ source venv/bin/activate 
$ pip install flask flask-login pysaml2 

마지막으로, 당신은이 작업을 위해 Okta 쪽 일을 수행해야합니다.

먼저 Okta 응용 프로그램 구성의 일반 탭에서 "성"및 "성"속성 명령문을 보내도록 응용 프로그램을 구성하십시오.Adding Attribute Statements to an Okta application

둘째 : 당신의 Okta 응용 프로그램 구성의 탭에서 단일 로그인으로, URL을 취하여 example.okta.com.metadata라는 이름의 파일에 넣어. 아래 명령과 같이 할 수 있습니다.

# -*- coding: utf-8 -*- 
import base64 
import logging 
import os 
import urllib 
import uuid 
import zlib 

from flask import Flask 
from flask import redirect 
from flask import request 
from flask import url_for 
from flask.ext.login import LoginManager 
from flask.ext.login import UserMixin 
from flask.ext.login import current_user 
from flask.ext.login import login_required 
from flask.ext.login import login_user 
from saml2 import BINDING_HTTP_POST 
from saml2 import BINDING_HTTP_REDIRECT 
from saml2 import entity 
from saml2.client import Saml2Client 
from saml2.config import Config as Saml2Config 

# PER APPLICATION configuration settings. 
# Each SAML service that you support will have different values here. 
idp_settings = { 
    u'example.okta.com': { 
     u"metadata": { 
      "local": [u'./example.okta.com.metadata'] 
     } 
    }, 
} 
app = Flask(__name__) 
app.secret_key = str(uuid.uuid4()) # Replace with your secret key 
login_manager = LoginManager() 
login_manager.setup_app(app) 
logging.basicConfig(level=logging.DEBUG) 
# Replace this with your own user store 
user_store = {} 


class User(UserMixin): 
    def __init__(self, user_id): 
     user = {} 
     self.id = None 
     self.first_name = None 
     self.last_name = None 
     try: 
      user = user_store[user_id] 
      self.id = unicode(user_id) 
      self.first_name = user['first_name'] 
      self.last_name = user['last_name'] 
     except: 
      pass 


@login_manager.user_loader 
def load_user(user_id): 
    return User(user_id) 


@app.route("/") 
def main_page(): 
    return "Hello" 


@app.route("/saml/sso/<idp_name>", methods=['POST']) 
def idp_initiated(idp_name): 
    settings = idp_settings[idp_name] 
    settings['service'] = { 
     'sp': { 
      'endpoints': { 
       'assertion_consumer_service': [ 
        (request.url, BINDING_HTTP_REDIRECT), 
        (request.url, BINDING_HTTP_POST) 
       ], 
      }, 
      # Don't verify that the incoming requests originate from us via 
      # the built-in cache for authn request ids in pysaml2 
      'allow_unsolicited': True, 
      'authn_requests_signed': False, 
      'logout_requests_signed': True, 
      'want_assertions_signed': True, 
      'want_response_signed': False, 
     }, 
    } 

    spConfig = Saml2Config() 
    spConfig.load(settings) 
    spConfig.allow_unknown_attributes = True 

    cli = Saml2Client(config=spConfig) 
    try: 
     authn_response = cli.parse_authn_request_response(
      request.form['SAMLResponse'], 
      entity.BINDING_HTTP_POST) 
     authn_response.get_identity() 
     user_info = authn_response.get_subject() 
     username = user_info.text 
     valid = True 
    except Exception as e: 
     logging.error(e) 
     valid = False 
     return str(e), 401 

    # "JIT provisioning" 
    if username not in user_store: 
     user_store[username] = { 
      'first_name': authn_response.ava['FirstName'][0], 
      'last_name': authn_response.ava['LastName'][0], 
      } 
    user = User(username) 
    login_user(user) 
    # TODO: If it exists, redirect to request.form['RelayState'] 
    return redirect(url_for('user')) 


@app.route("/user") 
@login_required 
def user(): 
    msg = u"Hello {user.first_name} {user.last_name}".format(user=current_user) 
    return msg 


if __name__ == "__main__": 
    port = int(os.environ.get('PORT', 5000)) 
    if port == 5000: 
     app.debug = True 
    app.run(host='0.0.0.0', port=port) 
+0

감사합니다 이것에 대한 많은 : 여기

Where to find the metadata url for an Okta application

$ curl [the metadata url for your Okta application] > example.okta.com.metadata 

는 IdP가이 SAML 요청을 시작 처리하는 파이썬/플라스크 응용 프로그램이 필요합니다 것입니다. 이것은 매우 도움이됩니다. 끝내주는 SP 시작 사례가 있다면! 또한 최신 버전의 pysaml2를 사용하고 있습니까? – steve

+0

당신을 진심으로 환영합니다! 예, 며칠 안에 SP로 시작한 예제를 추가 할 계획입니다. –

+0

pysaml2의 최신 버전이 최신 버전인지 확신 할 수 없습니다. 위의 예제에서 사용한 버전은'pip freeze'입니다.'pysaml2 == 2.2.0' –

관련 문제