GraphQL 인증 및 Graphene, SQLAlchemy 및 oso

GraphQL의 채택률은 줄곧 빠르게 상승하고 있다.이것은 개발자가 특정한 페이지에 필요한 데이터를 정확하게 지정하여 전단과 후단 코드 간의 인터페이스를 더욱 표현력 있게 할 수 있도록 한다.비록 전방 엔지니어가 많은 승리를 거두었지만 후방 팀은 다른 마음가짐을 필요로 한다.백엔드 팀은 고정 데이터 집합 (또는 일부 변체) 을 되돌려 주는 루트 처리 프로그램을 작성하지 않고 해석기 함수를 작성해야 한다.
많은 개발자들이 응용 프로그램에서 권한을 부여받는 출발점으로 루트 등급 검사를 하는 것을 보았다.흔히 볼 수 있는 모델은 루트 코드를 실행하기 전에 장식기나 다른 루트 단계의 보호를 사용하여 사용자가 특정한 요구를 충족시킬 수 있도록 하는 것이다(사용자는 관리자이고 사용자는 특정한 역할을 가진다).
이런 방법은 입문하기 쉬우나GraphQL에서는 통상적으로 불가능하다.GraphQL 응용 프로그램에는 잠재적인 데이터 집합이 고도로 변환될 수 있는 경로가 있습니다.GraphQL 쿼리의 필드 조합은 일반적인 REST 응용 프로그램보다 훨씬 많습니다.
GraphQL은 단일 데이터 요소에 대한 접근을 위한 것이기 때문에 권한을 수행하는 자연스러운 장소는 데이터 접근 기간이다.GraphQL의 공식 파일 승인:

Where should you perform validation and authorization checks? The answer: inside a dedicated business logic layer. Your business logic layer should act as the single source of truth for enforcing business domain rules [emphasis added].


https://graphql.org/learn/thinking-in-graphs/#business-logic-layer
위대했어쉬웠어그런데 이게 도대체 어떻게 된 일입니까?우리는 어떻게 업무 영역 규칙을 집행합니까?그것들을 지정하기 위한 간단한 추상이 있습니까?
본고에서 우리는 oso를 소스 권한 수여 라이브러리로 사용하여 권한 수여 규칙을 강제적으로 집행하는 방법을 소개할 것이다.우리는 oso의 정책 언어인 Polar로 권한 수여 규칙을 성명적으로 지정하는 방법과 몇 줄의 코드만으로 oso를GraphQL 응용 프로그램에 집적하는 방법을 보게 될 것이다.
우리는 SQLAlchemy를 ORM으로 사용하고Graphene - 유행하는 Python GraphQL 라이브러리를 사용할 것이다.sqlalchemy-oso library은 oso,GraphQL,SQLAlchemy 사이에 접착제를 제공하여 권한 수여 일치성을 강제로 집행할 수 있도록 합니다.

설정


비용 관리 애플리케이션을 나타내는 기본 Flask 애플리케이션부터 시작하겠습니다.다음은 clone the project on GitHub.이 프로그램의 권한 수여와 관련된 부분을 훑어보겠지만, 전체 코드는 프로젝트에서 얻을 수 있습니다.우리는 당신이 Flask, SQLAlchemy,GraphQL에 대해 어느 정도 알고 있다고 가정합니다.본 문서의 모든 부분은 저장소의 제출을 바탕으로 하고 이 부분의 시작은 관련 제출에 연결됩니다.
만약 읽는 과정에서 이 예시들을 계속 시도하고 시도할 계획이라면, 코드를 실행하기 위해 README에 의존항을 설치해야 합니다.이 섹션에서는 initial commit에 대해 설명합니다.
EMC 애플리케이션의 모델 번호는 다음과 같습니다.

  • 비용: 사용자가 만든 비용.

  • 사용자: 응용 프로그램에 액세스한 사용자입니다.
  • 우리는 이것부터 시작해서 잠시 후에 좀 더 추가할 것이다.다음은 최초의 models.py 모델입니다.이것들은 일반적인 연금술 모형들이오.flask_sqlalchemy 라이브러리를 Flask와 통합하여 설치를 단순화했습니다.
    from flask_sqlalchemy import SQLAlchemy
    
    from sqlalchemy.orm import relationship
    
    db = SQLAlchemy()
    
    class Expense(db.Model):
        __tablename__ = 'expenses'
    
        id = db.Column(db.Integer, primary_key=True)
        amount = db.Column(db.Integer)
    
        created_by_id = db.Column(db.Integer, db.ForeignKey('users.id'))
        created_by = relationship("User")
    
        description = db.Column(db.Text)
    
    class User(db.Model):
        __tablename__ = 'users'
    
        id = db.Column(db.Integer, primary_key=True)
        email = db.Column(db.String(256))
    
    비용은 사용자가 만들고 Expense.created_by 속성에서 획득할 수 있습니다.app/schema.py에서는 그래핀을 사용하여 이러한 모델에 GraphQL 모델을 정의했습니다.
    import graphene
    from graphene import relay
    from graphene_sqlalchemy import SQLAlchemyConnectionField, SQLAlchemyObjectType
    
    from flask import g
    
    from . import models
    
    class Expense(SQLAlchemyObjectType):
        class Meta:
            model = models.Expense
            interfaces = (relay.Node,)
    
    class User(SQLAlchemyObjectType):
        class Meta:
            model = models.User
            interfaces = (relay.Node,)
    
    class Query(graphene.ObjectType):
        expenses = SQLAlchemyConnectionField(Expense.connection)
        user = graphene.Field(User)
        node = graphene.relay.Node.Field()
    
        def resolve_user(parent, info):
            return (g.current_user
                    if isinstance(g.current_user, models.User)
                    else None)
    
    # ... snip ...
    
    schema = graphene.Schema(query=Query)
    
    이 모드는 Graphene-SQLAlchemy library을 사용하여 SQLAlchemy 모델에 그래핀 모드 대상을 만듭니다.SQLAlchemy와 그래핀을 결합해 사용하는 standard approach이다.
    우리는 간단한 조회를 할 수 있다. (나는 아주 좋은 GraphiQL 도구를 사용하지만, 너도 브라우저에서 http://localhost:5000/graphql으로 이동해서 우리의 응용 프로그램이 정상적으로 작동하는지 볼 수 있다.)

    라이센스 추가


    현재 GraphQL 응용 프로그램은 권한이 부여되지 않았습니다.우리 안배해 봅시다.commit on GitHub입니다.
    우리의 최초의 권한 수여 규칙은 데이터 소유자의 개념을 바탕으로 한다. 사용자는 자신의 비용을 볼 수 있다.
    이 규칙을 실현하기 위해서는 SQLAlchemy 단계에서 필터를 실행해야 합니다.즉,
  • 비용에 맞춤형 파서 추가
  • GraphQL 모드에서 액세스할 수 있는 모든 추가 위치 확인
  • 맞춤형 SQLAlchemy 쿼리
  • 은 한 번 검사할 때마다 모델과 권한을 부여한다.
  • 이 승인 규칙은 각 데이터베이스 레코드(객체 수준 권한이라고도 함)를 기반으로 합니다.그것을 중간부품으로 추상화하는 것은 도전일 수도 있고, 앞에서 논의한 간단한 장식기를 추가하는 것보다 훨씬 복잡할 것이다.
    대신 sqlalchemy-oso 라이브러리를 사용합니다.이 설정을 설정하려면 두 가지 변경 사항이 필요합니다.
  • Oso의 실례를 저희 프로그램에 추가합니다.
  • 은 권한 수여 규칙을 포함하는 전략을 작성한다.
  • oso를 추가하려면 create_app 함수를 수정합니다.
    from oso import Oso
    from sqlalchemy_oso import register_models
    
    def create_app():
        # snip ...
    
        # Create oso instance which will hold our policy.
        oso = Oso()  
        # Make SQLAlchemy models available in the policy.
        register_models(oso, db.Model)  
        # Load policy file into oso.
        oso.load_file(Path(__file__).parent / "policy.polar")   
    
        # ...
    
    그런 다음 애플리케이션의 SQLAlchemy 객체를 AuthorizedSQLAlchemy으로 교체합니다.models.py:
    from sqlalchemy_oso.flask import AuthorizedSQLAlchemy
    
    db = AuthorizedSQLAlchemy(
        get_oso=lambda: current_app.oso,
        get_user=lambda: getattr(g, "current_user", None),
        get_action=lambda: "read"
    )
    
    우리는 이 대상을 flask_sqlalchemy.SQLAlchemy이 아니라 사용한다.이것은 조회를 필터할 수 있는 SQLAlchemy Session object을 제공하여 현재 사용자와 조작 권한 수여 대상으로만 되돌려줍니다.
    우리의 정책 문건은 현재 비어 있다.
    이제 질의 결과를 검토해 보겠습니다.

    다시는 아무도 돌아오지 않을 것이다.왜?정책 파일이 비어 있습니다.Polar는 기본적으로 deny이므로 규칙을 추가해야 합니다.우리가 이렇게 하기 전에, 우리가 이것이 어떻게 일을 하는지 좀 봅시다.마지막 질의를 수행하는 데 사용된 SQL을 기록하는 엔드포인트가 있습니다.따라가는 경우 다음 도구를 사용하여 응용 프로그램을 실행합니다.
    $ FLASK_RUN_EXTRA_FILES=app/policy.polar FLASK_DEBUG=1 flask run
    
    GraphiQL에서 질의를 실행합니다.다음은 좋은 출발점이다.
    {
      expenses {
        edges {
          node {
            id
            description
            createdBy {
              email
            }
          }
        }
      }
    }
    
    지금 http://localhost:5000/sql을 방문하십시오.
    여기서 우리는 다음과 같이 보았다.
    SELECT count(*) AS count_1
      FROM (
            SELECT expenses.id AS expenses_id,
                   expenses.amount AS expenses_amount,
                   expenses.created_by_id AS expenses_created_by_id,
                   expenses.description AS expenses_description
              FROM expenses
             WHERE 0 = 1
             ORDER BY expenses.id ASC
           ) AS anon_1
    
    WHERE 자구: app/policy.polar을 참고하십시오.그거 어디서 났어요?0 = 1은 정책의 규칙을 SQL 필터로 변환하여 Polar 정책을 구현합니다.sqlalchemy-oso은 항상false로 데이터베이스에서 어떤 기록도 되돌려주지 않습니다.

    규칙 추가


    현재 우리는 oso를 통합하여 권한 수여 규칙의 실현을 완성했다.어플리케이션 실행을 유지합니다. 0 = 1 환경 변수를 설정하면 정책을 변경할 때 Flask에서 어플리케이션을 다시 로드할 수 있습니다.FLASK_RUN_EXTRA_FILES에서 다음을 시도해 보겠습니다.
    allow(_actor, _action, _resource);
    
    이 정책은 사용자가 모든 대상에 접근할 수 있도록 합니다.Polar에서 정책의 각 문은 규칙이라고 합니다.app/policy.polar 규칙은 특수한 규칙으로 전략의 입구점으로 사용된다.이것은 세 가지 인자가 있는데, actor (요청한 사람), action (요청이 무엇을 할 것인지), Resource (요청 작업의 대상) 이다.우리의 정책에는 규칙이 하나 있다.모든 매개 변수는 allow으로 시작하여 익명 변수를 표시합니다. (제약이 없기 때문에 모든 변수와 일치하는 변수입니다.)
    쿼리를 다시 실행하고 SQL을 확인하십시오(http://localhost:5000/sql을 새로 고치기만 하면 됩니다.GraphQL 쿼리를 해석하기 위한 주요 SQL 쿼리를 선택했습니다.
    SELECT expenses.id AS expenses_id,
           expenses.amount AS expenses_amount,
           expenses.created_by_id AS expenses_created_by_id,
           expenses.description AS expenses_description
      FROM expenses
     WHERE 1 = 1
     ORDER BY expenses.id ASC
     LIMIT 4
    OFFSET 0
    
    지금 우리는 _을 보았다.이것은 영원히 정말이다!결국 모든 물건이 돌려주었다.
    한 단계 더 나아가겠습니다.
    allow(_: User, "read", _: Expense);
    
    이 정책은 모든 사용자가 모든 비용을 읽을 수 있도록 규정하고 있다.마찬가지로 WHERE 1 = 1은 익명이나 무시된 변수를 표시하지만 _ 문법은 입력 데이터가 특정 유형과 일치하는지 검사합니다.이 예에서 사용자와 비용 모델은 우리의 models.py 파일에서 나온 것이다.
    다음은 반환된 데이터입니다.

    각 비용의 : User 필드는 현재 createdBy입니다.우리는 null 자원의 규칙이 없기 때문에 사용자는 어떠한 비용도 볼 수 없습니다.User에 규칙을 추가합니다.
    allow(_: User, "read", _: Expense);
    allow(_: User, "read", _: User);
    
    상술한 보증서는 this commit on GitHub.이다
    이제 사용자는 자신의 비용을 볼 수 있다는 규칙을 살펴봅시다.극좌표 규칙에는 User 키워드로 표시된 바디가 있을 수 있습니다.여기에는 사용자의 요구 사항을 충족하는 규칙이 있습니다. 비용이 사용자에 의해 만들어진 경우 사용자는 비용을 읽을 수 있습니다.
    allow(user: User, "read", expense: Expense) if
        expense.created_by = user;
    
    allow(_: User, "read", _: User);
    

    SQL:
    SELECT expenses.id AS expenses_id,
           expenses.amount AS expenses_amount,
           expenses.created_by_id AS expenses_created_by_id,
           expenses.description AS expenses_description
      FROM expenses
     WHERE 1 = expenses.created_by_id
     ORDER BY expenses.id ASC
     LIMIT 2
    OFFSET 0
    
    지금 우리는 if을 보았다.WHERE 1 = expenses.created_by_id은 현재 사용자의 id입니다.우리의 정책은 이미 SQL로 바뀌었다!this commit to run it 보기!

    한층 더


    지금까지 우리는 작은 정책 예시를 보여 주었다.oso는 GraphQL에서만 간단한 장식기를 사용할 수 없지만 권한 수여 규칙을 일치된 추상적으로 표현해 줍니다.그것을 실현하기 위해서, 우리는 해상도를 수정할 필요도, 새로운 해상도를 작성할 필요도 없다.Polar와GraphQL은 모두 문제 영역을 해결하는 성명적인 방법이다. Polar는 권한 수여 규칙을 성명하는 추상화를 제공하고GraphQL은 백엔드에서 데이터를 얻는 성명적인 추상화를 제공한다.
    자세한 내용을 보려면 샘플 애플리케이션 here에서 이 정책을 살펴보겠습니다.이 정책은 다음과 같다.
  • 사용 관계의 새로운 권한 수여 규칙: 사용자는 자신이 있는 프로젝트의 비용을 볼 수 있다.Polar는 권한 수여 정책에서 흔히 볼 수 있는 관계와 관련된 표현 규칙을 지원한다. (관계는 일반적으로 업무 영역을 반영하는 방식으로 사용자와 자원을 한데 묶는다.)
  • 사용 규칙은 권한 수여 개념(예를 들어 창설)을 요약한다.새로운 규칙을 작성하면 추상적인 기능을 사용하고 정책을 깔끔하게 유지할 수 있습니다. 이것은 일반적인 기능을 코드의 나머지 부분의 함수로 끌어올리는 것과 같습니다. (필경 Polar는 코드일 뿐입니다.)
  • 우리가 걸어온 길에 대해 더 많은 것을 알아야 한다.
  • 문서는 List FilteringSQLAlchemy으로 문의하십시오.

  • Install1고입니다.

  • Join our Slack 우리 엔지니어링 팀과 채팅.우리는 본문에서 토론한 기능을 빠르게 확장하고 있으며, 당신의 용례를 이해하기를 바랍니다.
  • 좋은 웹페이지 즐겨찾기