tailscale_rest/models/policy.rs
1//! The policy file, and what the control plane says about one.
2//!
3//! The policy itself is HuJSON — comments and trailing commas — so it is not a
4//! model: it travels as text, under `application/hujson`, and comes back as
5//! whatever the caller's `Accept` asked for. What is modelled here is
6//! everything *around* it — the answers `acl/preview` and `acl/validate` give,
7//! and the test case `acl/validate` takes — because those are ordinary JSON
8//! whatever format the document itself is in.
9
10use serde_json::Value;
11
12use crate::model;
13use crate::models::KnownValues;
14
15/// What a policy preview can be asked about.
16///
17/// `user` previews the rules that would match a user, `ipport` the rules that
18/// would match an address and port. The parameter is called `type`, which is
19/// why the constant is not.
20pub const PREVIEW_SUBJECTS: &[&str] = &["user", "ipport"];
21
22pub const KNOWN_VALUES: &[KnownValues] =
23 &[("/tailnet/{tailnet}/acl/preview ?type", PREVIEW_SUBJECTS)];
24
25model! {
26 /// What previewing a policy answers: the rules that would match, and the
27 /// question echoed back.
28 PolicyPreview as "POST /tailnet/{tailnet}/acl/preview 200" {
29 matches: "matches" => Vec<PolicyMatch>,
30 /// Echoes the `type` asked for.
31 subject_type: "type" => String,
32 /// Echoes the `previewFor` asked for.
33 preview_for: "previewFor" => String,
34 }
35
36 /// One rule that would match, and where in the document it is written.
37 PolicyMatch as "POST /tailnet/{tailnet}/acl/preview 200.matches[]" {
38 /// The sources the rule affects.
39 users: "users" => Vec<String>,
40 /// The destinations it reaches.
41 ports: "ports" => Vec<String>,
42 /// Which line of the policy file the rule is on, so that a caller can
43 /// go and read it.
44 line_number: "lineNumber" => i64,
45 }
46
47 /// What validating a policy answers *when something is wrong*.
48 ///
49 /// A pass is an empty body, so a caller that receives any of this has a
50 /// failure or a warning to read. `data` is left as [`Value`] because its
51 /// items differ per finding — a failed test carries `errors`, an
52 /// unsynced group carries `warnings` — and the description gives them no
53 /// properties at all.
54 PolicyValidation as "POST /tailnet/{tailnet}/acl/validate 200" {
55 /// `test(s) failed`, `warning(s) found`, and the like.
56 message: "message" => String,
57 /// One entry per finding, in the control plane's own shape.
58 data: "data" => Vec<Value>,
59 }
60
61 /// One test case, as `acl/validate` takes them.
62 ///
63 /// Modelled although it is only ever sent, because a caller writes these
64 /// by hand and a name the description does not have is a test that
65 /// silently does not run.
66 PolicyTest as "POST /tailnet/{tailnet}/acl/validate body (application/json)|oneOf[0][]" {
67 /// The identity the test runs as: an email address, a group, a tag or
68 /// a host.
69 src: "src" => String,
70 /// Posture attributes to evaluate posture conditions against, as
71 /// `{"node:os": "windows"}`. Only needed by a policy that has them.
72 src_posture_attrs: "srcPostureAttrs" => std::collections::BTreeMap<String, Value>,
73 /// `tcp`, `udp` and the rest. Omitted tests either.
74 proto: "proto" => String,
75 /// `host:port` destinations this identity must reach.
76 accept: "accept" => Vec<String>,
77 /// `host:port` destinations it must not.
78 deny: "deny" => Vec<String>,
79 }
80}