Skip to main content

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}