AppSync를 사용하여 계층형 액세스를 모델링하는 방법

나는 의료 허가를 관리하는 첫 번째 응용 프로그램을 개발하기 위해 미국 고객과 협력해 왔다.HIPAA 규범에 따라 사용자 데이터에 대한 무단 액세스를 허용하지 않는 것이 중요합니다.
응용 프로그램의 일부로서 우리는 고객 회사와 고객의 관리자가 사용할 관리 도구를 구축했다.본질적으로 우리는 관리 도구에서 세 가지 역할을 한다.

  • 관리자: 고객 회사의 관리자입니다.이 사용자들은 새 세입자를 추가하는 것을 포함하여 모든 기능에 접근할 수 있다.

  • 임차인: 고객 고객의 관리자이며 일반적으로 HIE(Health Information Exchange)입니다.HIE는 병원, 약국 등 여러 의료 보건 공급업체와 협력하여 데이터 교환을 추진할 것이다.따라서 HIE 관리자는 자신의 조직과 협력하는 모든 의료 보건 공급자와 관련된 정보를 방문할 수 있어야 한다.

  • 서비스 분야: 위의 HIE와 협력하는 의료 서비스 공급업체의 관리자입니다.그들은 자신의 조직과 환자와 관련된 정보만 방문할 수 있다.
  • 이것은 관련된 실체에 대한 지나친 간소화이지만, 우리가 모델링해야 할 다양한 접근층을 설명하는 데 도움이 된다.
  • Admin 사용자는 모든 컨텐츠에 액세스할 수 있습니다.
  • Tenant 사용자는 자신의 데이터와 부속 서비스 분야에 속하는 데이터에 접근할 수 있다.
  • Service Area 사용자는 자신의 데이터에만 접근할 수 있습니다.

  • 좋은 소식은 AppSync를 사용하면 Cognito를 사용하여 그룹 기반 인증을 쉽게 할 수 있다는 것이다.이것이 바로 우리가 이 프로젝트에서 AppSync over API Gateway를 사용하기로 결정한 이유입니다.
    그러나 GraphQL 모드에서 이러한 중첩 동작을 모델링하는 것 자체가 여전히 큰 도전이다.

    원인 중 하나 문제


    문제의 근원은 각 그룹이 많은 중첩된 조작을 할 수 있다는 데 있다.
    예를 들어, Admin 사용자는 새로운 서비스 영역을 추가하여 모든 Tenant 과 연결할 수 있습니다.Tenant 사용자도 새로운 서비스 구역을 추가할 수 있으나 서비스 구역을 자신 이외의 누구와도 연결할 수 없다.
    따라서 우리는 서로 다른 입력을 가진 두 개의 독립적인 돌연변이를 가지고 서로 다른 인지군에 대한 접근을 잠가야 한다.아래에서 알 수 있듯이 Tenant 사용자로서 나는 addServiceAreaAsTenant 변이에 접근할 수 있을 뿐, 거기에서 나는 서비스 구역과 관련된 tenantId을 덮어쓸 수 없다.
    type Mutation {
      addServiceAreaAsAdmin(tenantId: ID!, name: String!): ServiceArea!
      @aws_auth(cognito_groups: ["Admin"])
    
      addServiceAreaAsTenant(name: String!): ServiceArea!
      @aws_auth(cognito_groups: ["Tenant"])
    }
    
    우리가 점점 더 많은 조회와 돌변을 추가함에 따라 이것은 곧 혼란스러워질 것이다.@aws_auth(cognito_groups: …) 자구에는 중복된 항목이 많아서 각 그룹의 사용자가 무엇을 할 수 있는지 일목요연하게 알기 어렵다.그 밖에 우리는 이러한 조회와 돌연변이를 서로 구분하기 위해 어떻게 명명해야 하는지에 대해 점점 창의적이어야 한다.
    복사와 붙여넣기 오류는 쉽게 발생할 수 있고 (거의 이렇다) 전단 개발자들도 어떤 사용자를 위해 어떤 조회와 변체를 사용해야 하는지 추적하기 어렵다.
    schema {
      query: Query
      mutation: Mutation
    }
    
    type Query {
      getTenantById(id: ID!): Tenant!
      @aws_auth(cognito_groups: ["Admin"])
    
      getServiceAreaById(id: ID!): ServiceArea!
      @aws_auth(cognito_groups: ["Admin"])
    
      getMyProfileAsTenant: Tenant!
      @aws_auth(cognito_groups: ["Tenant"])
    
      getMyServiceArea(id: ID!): ServiceArea!
      @aws_auth(cognito_groups: ["Tenant"])
    
      getMyProfileAsServiceArea: ServiceArea!
      @aws_auth(cognito_groups: ["ServiceArea"])
    }
    
    type Mutation {
      addTenant(name: String!): Tenant!
      @aws_auth(cognito_groups: ["Admin"])
    
      addServiceAreaAsAdmin(tenantId: ID!, name: String!): ServiceArea!
      @aws_auth(cognito_groups: ["Admin"])
    
      updateMyProfileAsTenant(name: String!): Tenant!
      @aws_auth(cognito_groups: ["Tenant"])
    
      addServiceAreaAsTenant(name: String!): ServiceArea!
      @aws_auth(cognito_groups: ["Tenant"])
    
      updateMyProfileAsServiceArea(name: String!): ServiceArea!
      @aws_auth(cognito_groups: ["ServiceArea"])
    }
    
    type Tenant {
      id: ID!
      name: String!
    }
    
    type ServiceArea {
      id: ID!
      name: String!
      tenantId: ID!
    }
    

    네스트된 질의/변이 유형 사용


    이 프로젝트의 복잡성을 더욱 쉽게 확장하기 위해서, 우리는 각 그룹을 자신의 QueryMutation 유형으로 봉인하기로 결정했다.
    아래와 같이 각 그룹의 사용자는 자신의 QueryMutation 유형을 가지고 있다.액세스 제어는 한 곳에서 사용자가 할 수 있는 일을 한눈에 볼 수 있다.그 밖에 우리는 더 이상 중첩 동작의 명명 약정에 의존할 필요가 없다.
    schema { 
      query: Query
      mutation: Mutation
    }
    
    type Query {
      asAdmin: AdminQuery
      @aws_auth(cognito_groups: ["Admin"])
    
      asTenant: TenantQuery
      @aws_auth(cognito_groups: ["Tenant"])
    
      asServiceArea: ServiceAreaQuery
      @aws_auth(cognito_groups: ["ServiceArea"])
    }
    
    type Mutation {
      asAdmin: AdminMutation
      @aws_auth(cognito_groups: ["Admin"])
    
      asTenant: TenantMutation
      @aws_auth(cognito_groups: ["Tenant"])
    
      asServiceArea: ServiceAreaMutation
      @aws_auth(cognito_groups: ["ServiceArea"])
    }
    
    type AdminQuery {
      getTenant(id: ID!): Tenant!
      getServiceArea(id: ID!): ServiceArea!
    }
    
    type TenantQuery {
      getMyProfile: Tenant!
      getMyServiceArea(id: ID!): ServiceArea!
    }
    
    type ServiceAreaQuery {
      getMyProfile: ServiceArea!
    }
    
    type AdminMutation {
      addTenant(name: String!): Tenant!
      addServiceArea(tenantId: ID!, name: String!): ServiceArea!
    }
    
    type TenantMutation {
      updateMyProfile(name: String!): Tenant!
      addServiceArea(name: String!): ServiceArea!
    }
    
    type ServiceAreaMutation {
      updateMyProfile(name: String!): ServiceArea!
    }
    
    type Tenant {
      id: ID!
      name: String!
    }
    
    type ServiceArea {
      id: ID!
      name: String!
      tenantId: ID!
    }
    
    프로젝트가 끊임없이 증가함에 따라 GraphQL 모델은 기존의 안전 모델을 파괴하지 않고 유지보수하기 쉽다.Admin 사용자로서 나는 AdminQueryAdminMutation 유형에서 지정한 조회와 돌연변이를 실행할 수 있다.

    Tenant 사용자로서 내 조직의 tenantId 은 나의 Cognito 사용자의 사용자 정의 속성으로 포착되었다.
    Tenant 사용자의 조회와 돌연변이는 받아들여지지 않는다tenantId. 이것은 항상 $context.identity에서 나온 것이다.이것은 세입자가 자신의 데이터에만 접근할 수 있고 코드에 사용자 정의 검증 논리를 넣을 필요가 없다는 것을 확보했다.
    나는 Admin 그룹에 속하지 않기 때문에, 나는 그것의 조회와 돌연변이에 접근할 권리가 없다.

    하지만 저는 자신의 데이터를 처리하고 제 조직과 관련된 서비스 분야의 데이터를 방문할 수 있습니다.

    Service Area 사용자도 마찬가지다. 그들은 자신의 데이터에만 접근할 수 있다.
    또한 HIPAA 규정 준수는 사용자가 액세스한 데이터에 대한 감사 추적을 요청합니다.따라서 전체 요청 - 응답 로그 기록을 실현해야 합니다. 안타깝게도 AppSync는 이런 기능을 제공하지 않았습니다.우리는 뒤의 게시물에서 이 특정한 주제를 깊이 있게 탐구할 것이다.
    또한 이 차원 모델이 어떻게 자신을 위해 일하는지 보고 싶다면 을 보고 시도해 보세요.
    만약 실천 강좌를 통해 GraphQL과 AppSync를 배우고 싶다면 내 this repo 를 보세요. 그곳에서 AppSync, Lambda, DynamoDB, Cognito와 Vue의 조합으로 트위터 복제를 구축할 것입니다.js.다양한 GraphQL 모델링 기술, 해석기 디버깅 방법, CI/CD, 감시와 경보 등을 배울 것입니다.
    new AppSync course
    게시물이 먼저 How to model hierarchical access with AppSync에 올라왔다.

    좋은 웹페이지 즐겨찾기