2个不稳定版本

0.9.0-rc.12023年7月6日
0.7.0-dev.1 2022年10月23日
0.6.0-dev.1 2022年7月28日

#472 in 异步

Download history 8/week @ 2024-07-06 91/week @ 2024-07-27

99 每月下载量

MIT 许可证

78KB
1.5K SLoC

警告 ⚠️ ️: 此项目处于积极开发中

minos

Minos授权语言的授权库。此仓库包含语言定义、解析器和授权引擎。

Minos授权

...

Minos语言

Minos语言是一种领域特定语言,专为编写授权策略而创建。

要描述语法,我们首先从下一个文件开始

/* in example.minos */
syntax = 0.16;

/* Resource declaration */
resource User {
	/* Environment declaration */
    env DEFAULT {
    	/* policy declaration */ 
        policy {
        	/* Permissions granted if at least one rule is met */
            allow = ["create", "read", "update", "delete"];
			
			/* Rule declaration */
            rule {
                actor.type = RootUser;
            }

            rule {
                actor.type = resource.type;
                actor.id = resource.id;
            }
        }
    }
}

首先,Minos文件中的注释写在与CSS相同的字符 /**/ 之间。

/*in example.minos */

每个Minos文件需要在文档开头声明所使用的语法版本。

syntax = 0.16;

...

现在,我们可以回到整个文件

syntax = 0.16;

resource User {
    env DEFAULT {
        policy {
            allow = ["create", "read", "update", "delete"];
            rule {
                actor.type = RootUser;
            }
            rule {
                actor.type = resource.type;
                actor.id = resource.id;
            }
        }
    }
}

此块描述了一个名为 User 的资源,仅有一个名为 DEFAULT 的环境。重要的是要指出,如果存在 DEFAULT 块,它将在每个授权过程中应用。

隐式环境

基于以上内容,我们可以重写此块,不使用环境

resource User {
    policy {
        allow = ["create", "read", "update", "delete"];
        rule {
            actor.type = RootUser;
        }
        rule {
            actor.type = resource.type;
            actor.id = resource.id;
        }
    }
}

代码将被解析为前面的示例,但在此情况下,DEFAULT 块是隐式的。此功能称为“隐式默认”。

命名环境

环境为策略提供封装。为了排除“默认行为”模式,可以使用命名环境。我们可以修改示例,并定义两个环境: TestingProduction

resource User {
    env Testing {
        policy {
            allow = ["create", "read", "update", "delete"];
            rule {
                actor.type = RootUser;
            }
            rule {
                actor.type = resource.type;
                actor.id = resource.id;
            }
        }
    }
    env Production {
        policy {
            allow = ["create", "read", "update", "delete"];
            rule {
                actor.type = resource.type;
                actor.id = resource.id;
            }
        }
    }
}

我们可以看到,在 Testing 环境中, RootUser 可以操作 Users,但在 Production 中,只有 User 可以操作自己。

需要注意的是,当存在两个或多个环境时,无法使用隐式 DEFAULT。因为在这种情况下,DEFAULT 块不存在,并且在授权过程中需要指示环境名称。

现在,如果我们确定只需要两个环境,我们可以使用 DEFAULT 环境重写代码。

resource User {
    env DEFAULT {
        policy {
            allow = ["create", "read", "update", "delete"];
            rule {
                actor.type = resource.type;
                actor.id = resource.id;
            }
        }
    }
    env Testing {
        policy {
            allow = ["create", "read", "update", "delete"];

            rule {
                actor.type = RootUser;
            }
        }
    }
}

这种模式清楚地表明这是一个边缘情况,并允许我们在部署前移除Testing环境。但是,我们之所以这样做,仅仅是因为规则是兼容的,并且我们想要使用DEFAULT环境。

规范

如果我们需要根据资源本身而不是环境实现不同的行为呢?这可以通过使用规范功能和属性资源来实现。

以下示例中,我们可以定义两个不同的块

resource File {
    policy {
        allow = ["read"];
        rule {
            actor.type = User;
        }
    }
    policy {
        allow = ["write", "delete"];

        rule {
            actor.roles *= ["admin"];
        }
    }
}

resource File {
    id = "confidential.john.data.file.id";
    policy {
        allow = ["read"];

        rule {
            actor.id = "john.user.Id";
        }
    }
}

第一个块定义了一个名为File的资源。根据规则,用户可以读取File,但只有具有“admin”角色的actor才能写入或删除File

在第二个块中,我们发现对File的另一个声明。我们可以看到一些不同之处

  • 资源有一个名为id的属性,因此这是一个属性资源
  • 资源有一个包含冲突性规则的策略,因为在同一环境中,对“读取”权限的授权使用了两个策略。

在这种情况下,授权过程是如何工作的?如果资源是File并且其id是"confidential.john.data.file.id",则忽略File的规则,而只应用匹配id的策略。结果是,具有id "confidential.john.data.file.id"的File不能被覆盖或删除。

很简单,如果我们想为特定资源定义特殊规则,我们只需使用规范

规则中使用id属性

但是,如果我们想将规则用于DEFAULT环境而不是忽略它,我们可以像这样扩展第一个块

resource File {
    policy {
        allow = ["read"];
        rule {
            actor.type = User;
        }
        /* start of adition --- */
        rule {
        	resource.id = "confidential.john.data.file.id";
        	actor.id = "john.user.Id";
        }
        /* --- end of adition */
    }
    policy {
        allow = ["write", "delete"];

        rule {
            actor.roles *= ["admin"];
        }
    }
}

上面的代码将被解析,因为具有id "john.user.Id"的actor可以读取具有id "confidential.john.data.file.id"的File。我们可以看到,如果actor是User,则新规则实际上是无用的。这是使用actor.id和/或resource.id在同一环境中的问题:严格规则可能会被更宽松的规则跳过。

属性比较

在最后一个示例中,使用id属性的情况仅适用于actor不是User的情况。因此,我们可以使用上述代码处理以下数据

{
    "actor": {
    	"id": "john.user.Id",
        "type": "Employee",
        "groups": ["employees"],
        "roles": ["admin"]
    },
    "resource": {
        "id": "confidential.john.data.file.id",
        "type": "File",
        "owner": "john.user.Id"
    },
    "permissions": ["read"]
}

授权已授予,因为规则正好涵盖了这种情况。那么,如果我们为每位员工都有一个机密文件怎么办?我们必须为每个文件-员工对编写一个规则吗?不一定,我们可以使用actor.owner属性,这在当前minos语言版本(v0.16)中已经得到了很好的支持。

resource File {
    policy {
        allow = ["read"];
        rule {
            actor.type = User;
        }
        /* start of change --- */
        rule {
        	resource.owner = actor.id;
        }
        /* --- end of change */
    }
    policy {
        allow = ["write", "delete"];

        rule {
            actor.roles *= ["admin"];
        }
    }
}

此外,还可以通过添加actor.type要求来增强规则

/*... */
rule {
    actor.type = Employee;
	resource.owner = actor.id;
}
/*...*/

属性比较是避免常见逻辑重复的好方法。不幸的是,在当前minos语言版本(v0.16)中,支持的属性并不多。但我们希望在未来版本中改进对该功能的支持。

解析规则

在此阶段,重要的是解释minos解析器如何处理具有相同标识符的块。例如,[本节](#Use of id attribute in rules)的代码可以重写如下

resource File {
    policy {
        allow = ["read"];
        rule {
            actor.type = User;
        }
        rule {
        	resource.id = "confidential.john.data.file.id";
        	actor.id = "john.user.Id";
        }
    }
}

resource File {
    policy {
        allow = ["write", "delete"];

        rule {
            actor.roles *= ["admin"];
        }
    }
}

minos是如何解析上述文本的?

  1. 搜索资源。
  2. 添加File资源。
  3. 搜索环境。
  4. 添加隐式DEFAULT环境。
  5. 搜索策略。
  6. 添加策略以允许权限 ["read"] 和两条规则。
  7. 搜索更多策略。
  8. 跳过搜索更多环境,因为该块使用隐式 DEFAULT
  9. 搜索更多资源。
  10. 找到 文件 资源。
  11. 搜索环境。
  12. 找到隐式 DEFAULT 环境。
  13. 搜索策略。
  14. 添加策略以允许权限 ["write", "delete"] 和一条规则。
  15. 搜索更多策略。
  16. 跳过搜索更多环境,因为该块使用隐式 DEFAULT
  17. 搜索更多资源 ...

此行为与其他文件中的资源相同。例如,我们可以将上面的代码重写如下

/* in file.minos */
resource File {
    policy {
        allow = ["read"];
        rule {
            actor.type = User;
        }
    }
}
/* in file2.minos */
resource File {
    policy {
        allow = ["read"];        
        rule {
        	resource.id = "confidential.john.data.file.id";
        	actor.id = "john.user.Id";
        }
    }
}
/* in file3.minos */
resource File {
    policy {
        allow = ["write", "delete"];

        rule {
            actor.roles *= ["admin"];
        }
    }
}

最终的解析,将与此节第一个示例完全相同。

依赖项

~7MB
~132K SLoC