Фильтрация наборов данных на основе политик OPA - PullRequest
0 голосов
/ 17 апреля 2020

У меня есть модель управления доступом, которая состоит из двух уровней: Уровни доступа и Общие разрешения .

Пользовательский Уровень доступа управляет как максимальным набором разрешений, которые вы можете иметь в системе, так и предоставляет вам базовые c разрешения для создания объектов верхнего уровня в системе ( портфолио ios, программы, проекты и др. c ..). Объекты в системе также могут быть переданы вам кем-то еще в системе (таким образом, предоставляя вам одно или несколько разрешений конкретно для объекта). Если объект не был создан вами или не был назначен вам, вы сможете увидеть его, если он явно не предоставлен вам. Пример набора данных будет выглядеть примерно так:

"access_levels": {
    "Worker": ["projects.view"],
    "Planner": ["projects.create", "projects.edit", "projects.view"]
},
"users_access_level": {
    "bob.jones@example.com": "Planner",
    "joe.humphreys@example.com": "Worker"
},
"resource_hierarchy": {
    "customer1": ["customer1"],
    "project1": ["project1", "customer1"],
    "project2": ["project2", "customer1"]
},
"resource_policyIDs": {
    "customer1": "1",
    "project1": "2",
    "project2": "3",
},
"policies": {
    "1": {
        "permissions": ["projects.create"],
        "subjects": ["users:joe.humphreys@example.com"]
    },
    "2": {
        "permissions": ["projects.view"],
        "subjects": ["users:joe.humphreys@example.com"]
    },
    "3": {}
}

, а политика будет выглядеть примерно так:

package authz

default authorized = false

authorized {
    input.method == "POST"
    http_path = ["programs", "create"]
    input.customerId == token.payload.customerId
    permitted_actions(token.payload.sub, input.customerId)[_] == "programs.create"
}

subjects_resource_permissions(sub, resource) = { perm |         
    resource_ancestors := data.resource_hierarchy[resource]         
    ancestor := resource_ancestors[_]                           
    id := data.resource_policyIDs[ancestor]                         
    data.policies[id]["subjects"][_] == sprintf("users:%v", [sub])
    perm := data.policies[id]["permissions"][_]                     
}

permitted_actions(sub, resource) = x {
    resource_permissions := subjects_resource_permissions(sub, resource)
    access_permissions := data.access_levels[data.users_access_level[sub]]

    perms := {p | p := access_permissions}

    x = perms & resource_permissions
}

http_path := split(trim_left(input.path, "/"), "/")

Допустим, я создал API Projects для управления проектом Ресурсы. В API есть метод для перечисления проектов, и метод должен возвращать только те проекты, к которым у пользователя есть доступ к представлению. Таким образом, в приведенном выше примере пользователь «joe. humphreys@example.com» не должен иметь возможность просматривать Project 2, даже если его уровень доступа дает ему «projects.view». Это связано с тем, что им с ним не поделились.

Если бы я хотел использовать OPA, как я мог бы предоставить общий шаблон для выполнения sh в нескольких API? Как бы выглядел запрос к OPA для выполнения sh что-то вроде этого? Другими словами, если бы я хотел применить эту авторизацию в SQL, как бы это выглядело?

Я прочитал эту статью , но мне трудно понять, как это подходит здесь ..

1 Ответ

1 голос
/ 21 апреля 2020

Я предполагаю, что ваша двухслойная модель И 'Уровень доступа' с 'Общим разрешением'. Например, "joe" может видеть "project1", потому что "joe" является рабочим, поэтому он имеет разрешение "projects.view" И"joe" назначен для "project1" (через политику "2") с разрешение "projects.view". Поскольку "joe" не назначен для "project2" посредством какой-либо политики с разрешением "projects.view", "joe" не может видеть "project2". То есть, даже если "joe" был назначен для "project2" через какую-то политику, эта политика должна указывать разрешение "projects.view", в противном случае "joe" не сможет его увидеть.

Вы может написать что-то вроде этого, чтобы сгенерировать набор ресурсов проекта, которые субъект может видеть:

authorized_project[r] {

    # for some projects resource 'r', if...
    r := data.projects[_]

    # subject has 'projects.view' access, and...
    level := data.user_access_levels[input.sub]
    "projects.view" == data.access_level_permissions[level][_]

    # subject assigned to project resource (or any parents)
    x := data.resource_hierarchy[r.id][_]
    p := data.resource_policies[x]
    "projects.view" == data.policies[p].permissions[_]
    input.sub == data.policies[p].subjects[_]
}

. Возникает вопрос, как можно заполнить data.projects, data.policies и data.resource_hierarchy ( Я предполагаю, что наборы данных уровня доступа намного меньше, но с ними также может возникнуть тот же вопрос.) В блоге (на который вы ссылались) обсуждаются ответы на этот вопрос. Обратите внимание, что передача данных через input вместо data на самом деле ничего не меняет - они все равно должны быть доступны при каждом запросе.

Вы можете изменить код, приведенный выше, и сделать его немного более читабельным:

authorized_project[r] {
    r := data.projects[_]
    subject_access_level[[input.sub, "projects.view"]]
    subject_shared_permission[[input.sub, "projects.view", r.id]]
}

subject_access_level[[sub, perm]] {
    some sub
    level := data.user_access_levels[sub]
    perm := data.access_level_permissions[level][_]
}

subject_shared_permission[[sub, perm, resource]] {
    some resource
    x := data.resource_hierarchy[resource][_]
    p := data.resource_policies[x]
    perm := data.policies[p].permissions[_]
    sub := data.policies[p].subjects[_]
}

Вы можете обобщить вышесказанное следующим образом:

authorized_resource[[r, kind]] {
    r := data.resources[kind][_]
    perm := sprintf("%v.view", [kind])
    subject_access_level[[input.sub, perm]]
    subject_shared_permission[[input.sub, perm, r.id]]
}

subject_access_level[[sub, perm]] {
    some sub
    level := data.user_access_levels[sub]
    perm := data.access_level_permissions[level][_]
}

subject_shared_permission[[sub, perm, resource]] {
    some resource
    x := data.resource_hierarchy[resource][_]
    p := data.resource_policies[x]
    perm := data.policies[p].permissions[_]
    sub := data.policies[p].subjects[_]
}
...