Why my authorization rules can't work

I might be able to help propose a better model/auth rule if you were to actually post your whole schema. There is a lot of undefined types here still.

And can you explain the purpose of needing school and categories in your rule? I can assume, but better would be if you explain it to me like I am 5 what you are trying to do here.

ok, Classes belong to Schools,and a classes have courses and sections, classes belong to a category emmm, every school have many classes, and auth rule have two method:

  1. If the user have full controller of the school, it can do everything in this school, like, add a course, add a classes…
  2. If the user don’t have full controller of the school, now i have to judge does he have access to operate the category of this school.

category likes Primary School, Middle School, University
classes likes class one, grade one. class two, grade one
courses like math, art, physical…

and the full schema like this.

interface Base {
    created_at: DateTime! @search(by: [day])
    updated_at: DateTime @search(by: [day])
    deleted_at: DateTime @search
    created_by: Int @search
    updated_by: Int @search
    deleted_by: Int @search
}

type Area {
    code: String! @id
    name: String!
    short_name: String!
    city_code: String
    zip_code:String
    merger_name:String!
    lng: String
    lat: String
    pinyin: String
    parent: Area @hasInverse(field:"child")
    child: [Area] @hasInverse(field:"parent")
    schools:[School] @hasInverse(field:"area")
}

#query ($user_id: String!,$role: String!){
#    queryClasses(filter: { has } }) {
#        id
#        school {
#            administrator {
#                id
#                user(filter: { mid: { eq: $user_id}}) {
#                    id
#                }
#                school_full_controller
#                category {
#                    id
#                }
#            }
#        }
#    }
#}


enum SchoolType {
    province
    city
    area
}

enum SchoolYYMode {
    """第三种方式"""
    third_party
    """加盟"""
    league
    """总校直营"""
    hq
    """外机构"""
    outside
}

enum SchoolStatus {
    """对应1 正常"""
    normal
    """对应2 删除"""
    deleted
    """对应3 未完善"""
    not_impl
}


type School implements Base{
    id: ID!
    mid: Int
    """学校名称"""
    school_name: String! @search
    """学校地址"""
    address: String!
    """联系电话"""
    phone: String! @search
    """成立日期"""
    date: DateTime
    """管理人/联系人"""
    admin: String
    """学校类型"""
    school_type: SchoolType @search
    """学校运营类型"""
    yy_mode: SchoolYYMode @search
    """简介"""
    brief: String
    """学校状态"""
    status: SchoolStatus @search
    """版本号"""
    version: Int
    """拥有班级"""
    classes: [Classes]
    """所属地区"""
    area:Area! @hasInverse(field:"schools")
    """管理员"""
    administrator:[UserAuthSchoolClasses] @hasInverse(field:"school")
}

"""科目表:"""
type Subject implements Base {
    id: ID!
    mid: Int
    subject_name: String!
    color: String!
    teachers: [Teacher]
    user_sort:[SortSubjectUser] @hasInverse(field:"subject")
}


"""研究院表:"""
type Academy implements Base{
    id: ID!
    mid: Int
    academy_name:String! @search
    province:String!
    manager:Int!
    introduce:String
    phone:String!
    managers:String
    child_academy: [Academy] @hasInverse(field: "parent_academy")
    parent_academy: Academy @hasInverse(field: "child_academy")
    teachers: [Teacher] @hasInverse(field: "academy")
}

"""招考类型表:事业单位,省考,选调生,银行招考......"""
type RecruitingType implements Base{
    id: ID!
    mid: Int
    name: String! @search
    color: String! @search
}

"""班型表:刷题班,基础班,基础刷题班,冲刺班......"""
type ClassType implements Base{
    id: ID!
    mid: Int
    name: String! @search
    short_name: String! @search
}

"""课程类型:直播课,地面课......"""
type CourseType implements Base{
    id: ID!
    mid: Int
    name: String! @search
    color: String! @search
}

"""考试类型:面试,笔试......"""
type ExamType implements Base{
    id: ID!
    mid: Int
    name: String! @search
    color: String! @search
}

type Category implements Base{
    id: ID!
    mid: Int
    name: String! @search
    color: String! @search
}

"""班级"""
type Classes implements Base{
#@auth(
#    query: { rule:"""
#    query ($user_id: String!){
#    queryClasses {
#    id
#    school(filter:{id:"0x4e51"}){
#    id
#    administrator(
#    filter: {
#    or: [{ has: categories }, { school_full_controller : true }]
#    }
#    ) {
#    id
#    user(filter:{mid:{eq:$user_id}}) {
#    mid
#    }
#    school_full_controller
#    categories {
#    id
#    }
#    }
#    }
#    }
#    }
#    """
#    }
#) {
    id: ID!
    name: String! @search
    year: Int! @search
    class_type: ClassType!
    school: School! @hasInverse(field: "classes")
    recruiting_type: RecruitingType!
    is_full_day: Boolean! @search
    start_date: DateTime! @search
    end_date: DateTime! @search
    course_type: CourseType!
    exam_type: ExamType!
    category: Category!
    lend_teacher: Boolean!
    courses: [Course] @hasInverse(field: "classes")
    course_arrange: String
}

"""课程"""
type Course implements Base {
    id: ID!
    name: String! @search
    subject: Subject!
    school: School!
    classes: Classes! @hasInverse(field: "courses")
    sections: [CourseSection] @hasInverse(field: "course")
    remark: String
    finish: Boolean! @search
    start_date: DateTime! @search
    end_date: DateTime! @search
    introduce: String
    virtual: Boolean!
    lend_teacher: Boolean!
    share: Boolean!
    merge: Boolean!
    people_num: Int
}

"""节"""
type CourseSection implements Base {
    id: ID!
    name: String! @search
    course: Course! @hasInverse(field: "sections")
    identity: String!
    date: DateTime! @search
    start_time: DateTime! @search
    end_time: DateTime! @search
    teacher: [TeacherCourseInfo]
    time_duration: Float
    is_full_day: Boolean!
}

type TeacherWorkingMechanism implements Base{
    id: ID!
    mid: Int
    name: String! @search
    parent: TeacherWorkingMechanism @hasInverse(field: "child")
    child: [TeacherWorkingMechanism] @hasInverse(field: "parent")
    teachers: [Teacher] @hasInverse(field: "working_mechanism")
}

type TeacherPosition implements Base{
    id: ID!
    mid: Int
    name: String! @search
    teacher:[Teacher] @hasInverse(field: "position")
}

type TeacherBankAccount implements Base{
    id: ID!
    mid: Int
    bank_name: String! @search
    account: String! @search
    owner:[Teacher] @hasInverse(field: "bank_account")
}

type National {
    id: ID!
    name: String! @search
}

enum Sex {
    Man
    Women
}

"""教师"""
type Teacher implements Base{
    id:ID!
    """教师姓名"""
    teacher_name:String
    """年龄"""
    age:Int
    """艺名"""
    stage_name:String!
    """研究院"""
    academy:Academy
    """是否本校教师"""
    regular_employee: Boolean
    """是否试用期"""
    probation: Boolean
    #    """教师分组"""
    #    teacher_group:TeacherGroup
    """是否班主任"""
    head_teacher:Boolean
    """转正日期"""
    becoming_regular_date:DateTime
    """头像"""
    avatar:String
    """手机号"""
    mobile:String
    """微信号"""
    wechat:String
    """工作机制"""
    working_mechanism: TeacherWorkingMechanism @hasInverse(field: "teachers")
    """教师职位"""
    position: TeacherPosition @hasInverse(field: "teacher")
    """简介"""
    biography: String
    """教师性别"""
    sex: Sex
    """民族"""
    national: National
    """在职状态"""
    status: Boolean
    """身份证号"""
    id_card: String @search
    """银行卡"""
    bank_account: [TeacherBankAccount] @hasInverse(field: "owner")
    """上课时长"""
    class_hours: Float
    """工资"""
    wages: Float
    """离职日期"""
    quit_at: DateTime
    """入职日期"""
    entry_at: DateTime
    """是否实时"""
    is_realtime:Boolean
    """退款状态"""
    refund_status:Boolean
    """余额"""
    balance:Float
}


enum TeacherIdentity {
    """主讲"""
    speaker
    """助教"""
    TA
    """搭档"""
    partner
    """助教(学)"""
    TAStu
}


type TeacherCourseInfo implements Base {
    id:ID!
    course_section: CourseSection
    teacher: Teacher
    identity: TeacherIdentity
    """true-课老师,false-节老师"""
    is_course: Boolean
    class_pay: Float
    settlement_standard:String
}

enum UserAuthRole {
    admin
    user
    finance_user
}

type UserAuth {
    mid : String! @id
    schools:[UserAuthSchoolClasses]  @hasInverse(field:"user")
    role: UserAuthRole! @search
}

type UserAuthSchoolClasses {
    id: ID!
    school:School! @hasInverse(field:"administrator")
    school_full_controller:Boolean! @search
    categories:[Category]
    user: UserAuth! @hasInverse(field:"schools")
}

# Dgraph.Authorization {"VerificationKey":"xtjpk","Header":"x-d-token","Namespace":"xtjpk","Algo":"HS256","Audience":["user"]}

1 Like

I’m sorry about bother you, I’m trying to let the rule apply whole day, but I got noting :sob:

I will update tonight, I’ve been at work and busy yesterday

Thank u very much. It’s so nice to have a partner like you :joy:

I tested that this shcema loads, but I have not loaded data into the graph to test if the rules work (they should)

interface Base {
    ############################################################
	# granted_access is used for ACL traversing in @auth rules #
	############################################################
    granted_access: [AccessControl]
    created_at: DateTime! @search(by: [day])
    updated_at: DateTime @search(by: [day])
    deleted_at: DateTime @search
    created_by: Int @search
    updated_by: Int @search
    deleted_by: Int @search
}

type Area {
    code: String! @id
    name: String!
    short_name: String!
    city_code: String
    zip_code:String
    merger_name:String!
    lng: String
    lat: String
    pinyin: String
    parent: Area @hasInverse(field:"child")
    child: [Area] @hasInverse(field:"parent")
    schools:[School] @hasInverse(field:"area")
}

enum SchoolType {
    province
    city
    area
}

enum SchoolYYMode {
    """第三种方式"""
    third_party
    """加盟"""
    league
    """总校直营"""
    hq
    """外机构"""
    outside
}

enum SchoolStatus {
    """对应1 正常"""
    normal
    """对应2 删除"""
    deleted
    """对应3 未完善"""
    not_impl
}


type School implements Base{
    id: ID!
    mid: Int
    """学校名称"""
    school_name: String! @search
    """学校地址"""
    address: String!
    """联系电话"""
    phone: String! @search
    """成立日期"""
    date: DateTime
    """管理人/联系人"""
    admin: String
    """学校类型"""
    school_type: SchoolType @search
    """学校运营类型"""
    yy_mode: SchoolYYMode @search
    """简介"""
    brief: String
    """学校状态"""
    status: SchoolStatus @search
    """版本号"""
    version: Int
    """拥有班级"""
    classes: [Classes]
    """所属地区"""
    area:Area! @hasInverse(field:"schools")
	###############################################################################
	# Using the Base.granted_access edge instead of School.administrator for ACL. #
	###############################################################################
    #"""管理员"""
    #administrator:[UserAuthSchoolClasses] @hasInverse(field:"school")
	has_categories: [Category] @hasInverse(field: "in_school")
}

"""科目表:"""
type Subject implements Base {
    id: ID!
    mid: Int
    subject_name: String!
    color: String!
    teachers: [Teacher]
	# FIXME: This type is undefined
    # user_sort:[SortSubjectUser] @hasInverse(field:"subject")
}


"""研究院表:"""
type Academy implements Base{
    id: ID!
    mid: Int
    academy_name:String! @search
    province:String!
    manager:Int!
    introduce:String
    phone:String!
    managers:String
    child_academy: [Academy] @hasInverse(field: "parent_academy")
    parent_academy: Academy @hasInverse(field: "child_academy")
    teachers: [Teacher] @hasInverse(field: "academy")
}

"""招考类型表:事业单位,省考,选调生,银行招考......"""
type RecruitingType implements Base{
    id: ID!
    mid: Int
    name: String! @search
    color: String! @search
}

"""班型表:刷题班,基础班,基础刷题班,冲刺班......"""
type ClassType implements Base{
    id: ID!
    mid: Int
    name: String! @search
    short_name: String! @search
}

"""课程类型:直播课,地面课......"""
type CourseType implements Base{
    id: ID!
    mid: Int
    name: String! @search
    color: String! @search
}

"""考试类型:面试,笔试......"""
type ExamType implements Base{
    id: ID!
    mid: Int
    name: String! @search
    color: String! @search
}

type Category implements Base {
    id: ID!
    mid: Int
    name: String! @search
    color: String! @search
	in_school: School
}

"""班级"""
type Classes implements Base @auth(
  add: {
    or: [
	  # grant access to add if the class is for a school which has granted access to the user that is an admin
	  { rule: "query ($mid: String!) { queryClasses { school { granted_access { user(filter: { mid: { eq: $mid } role: { eq: admin } }) { id } } } } }" },
	  
	  # grant access to add if the class is for a school which has granted full access to the user
	  { rule: "query ($mid: String!) { queryClasses { school { granted_access(filter: { level: { eq: FULL_CONTROL } }) { user(filter: { mid: { eq: $mid } }) { id } } } } }" },
	  
	  # grant access to add if the class is in an category that has granted full access to the user and the user is granted any kind of access to the linked school
	  { rule: "query ($mid: String!) { queryClasses { category { granted_access(filter: { level: { eq: FULL_CONTROL } }) { user(filter: { mid: { eq: $mid } }) { id } } } school { granted_access { user(filter: { mid: { eq: $mid } }) { id } } } } }" },
	  
	  # (optional) grant access to a system script to load data
	  { rule: "{$USER: { eq: \"SYSTEM\" }}" }
	]
  }
) {
    id: ID!
    name: String! @search
    year: Int! @search
    class_type: ClassType!
    school: School! @hasInverse(field: "classes")
    recruiting_type: RecruitingType!
    is_full_day: Boolean! @search
    start_date: DateTime! @search
    end_date: DateTime! @search
    course_type: CourseType!
    exam_type: ExamType!
    category: Category!
    lend_teacher: Boolean!
    courses: [Course] @hasInverse(field: "classes")
    course_arrange: String
}

"""课程"""
type Course implements Base {
    id: ID!
    name: String! @search
    subject: Subject!
    school: School!
    classes: Classes! @hasInverse(field: "courses")
    sections: [CourseSection] @hasInverse(field: "course")
    remark: String
    finish: Boolean! @search
    start_date: DateTime! @search
    end_date: DateTime! @search
    introduce: String
    virtual: Boolean!
    lend_teacher: Boolean!
    share: Boolean!
    merge: Boolean!
    people_num: Int
}

"""节"""
type CourseSection implements Base {
    id: ID!
    name: String! @search
    course: Course! @hasInverse(field: "sections")
    identity: String!
    date: DateTime! @search
    start_time: DateTime! @search
    end_time: DateTime! @search
    teacher: [TeacherCourseInfo]
    time_duration: Float
    is_full_day: Boolean!
}

type TeacherWorkingMechanism implements Base{
    id: ID!
    mid: Int
    name: String! @search
    parent: TeacherWorkingMechanism @hasInverse(field: "child")
    child: [TeacherWorkingMechanism] @hasInverse(field: "parent")
    teachers: [Teacher] @hasInverse(field: "working_mechanism")
}

type TeacherPosition implements Base{
    id: ID!
    mid: Int
    name: String! @search
    teacher:[Teacher] @hasInverse(field: "position")
}

type TeacherBankAccount implements Base{
    id: ID!
    mid: Int
    bank_name: String! @search
    account: String! @search
    owner:[Teacher] @hasInverse(field: "bank_account")
}

type National {
    id: ID!
    name: String! @search
}

enum Sex {
    Man
    Women
}

"""教师"""
type Teacher implements Base{
    id:ID!
    """教师姓名"""
    teacher_name:String
    """年龄"""
    age:Int
    """艺名"""
    stage_name:String!
    """研究院"""
    academy:Academy
    """是否本校教师"""
    regular_employee: Boolean
    """是否试用期"""
    probation: Boolean
    #    """教师分组"""
    #    teacher_group:TeacherGroup
    """是否班主任"""
    head_teacher:Boolean
    """转正日期"""
    becoming_regular_date:DateTime
    """头像"""
    avatar:String
    """手机号"""
    mobile:String
    """微信号"""
    wechat:String
    """工作机制"""
    working_mechanism: TeacherWorkingMechanism @hasInverse(field: "teachers")
    """教师职位"""
    position: TeacherPosition @hasInverse(field: "teacher")
    """简介"""
    biography: String
    """教师性别"""
    sex: Sex
    """民族"""
    national: National
    """在职状态"""
    status: Boolean
    """身份证号"""
    id_card: String @search
    """银行卡"""
    bank_account: [TeacherBankAccount] @hasInverse(field: "owner")
    """上课时长"""
    class_hours: Float
    """工资"""
    wages: Float
    """离职日期"""
    quit_at: DateTime
    """入职日期"""
    entry_at: DateTime
    """是否实时"""
    is_realtime:Boolean
    """退款状态"""
    refund_status:Boolean
    """余额"""
    balance:Float
}


enum TeacherIdentity {
    """主讲"""
    speaker
    """助教"""
    TA
    """搭档"""
    partner
    """助教(学)"""
    TAStu
}


type TeacherCourseInfo implements Base {
    id:ID!
    course_section: CourseSection
    teacher: Teacher
    identity: TeacherIdentity
    """true-课老师,false-节老师"""
    is_course: Boolean
    class_pay: Float
    settlement_standard:String
}

enum UserAuthRole {
    admin
    user
    finance_user
}

###########################################################################################
# I am assuming this would be the type I refer to as "User" unique to an individual logon #
###########################################################################################
type UserAuth {
    ############################################################
    # NOTE: you can have both the @id and an ID field together #
	############################################################
    id: ID!
    mid: String! @id
	##################################################
	# Don't need specific access to the schools here #
	##################################################
    #schools:[UserAuthSchoolClasses]  @hasInverse(field:"user")
    role: UserAuthRole! @search
	##########################################################################
	# Reverse traversing of which resources the user has been granted access #
	##########################################################################
	has_access: [AccessControl]
}


################################
# Don't even need this anymore #
################################
#type UserAuthSchoolClasses {
#    id: ID!
#    school:School! @hasInverse(field:"administrator")
#    school_full_controller:Boolean! @search
#    categories:[Category]
#    user: UserAuth! @hasInverse(field:"schools")
#}

enum ControlLevel {
    FULL_CONTROL
	EDITOR
	VIEWER
}

###################################################
# Add nodes here for new user/access combinations #
###################################################
type AccessControl @auth(
  ####################################################
  # Make this inaccessible to query outside of rules #
  # NOTE: If you want to test query, you will need   #
  #  to comment this rule out temporarily            #
  ####################################################
  query: { rule: "{$DENIED: { eq: \"DENIED\" }}" }
  # FIXME: write a "add" rule or else anyone can create their own AccessControl granting themselves or anyone else access
  # FIXME: write a "delete" rule or else anyone can delete anyone else's access
  # FIXME: write a "update" rules or else anyone can steal anyone else's access
) {
	id: ID!
	######################################################################################
	# Link to the interface of the implementing node which the user is granted to access #
	######################################################################################
	controls: [Base] @hasInverse(field: "granted_access")
	user: UserAuth! @hasInverse(field: "has_access")
	##################################################################
	# What type of control to grant, useful for different CRUD rules #
	##################################################################
	level: ControlLevel @search
}

# Dgraph.Authorization {"VerificationKey":"xtjpk","Header":"x-d-token","Namespace":"xtjpk","Algo":"HS256","Audience":["user"]}

Look at the new type AccessControl which is linked to through Base which is implemented by Classes (should be Class probably) so the rule can then use those nodes for traveral.

Thank u.
I’ll fix my schema today, and try to understand the flow. By the way, I cloned dgraph in github and try to find auth module, but I think I can read this when I understand graphql fully. :sob: