Skip to main content

tailscale_rest/models/
tailnet.rs

1//! The tailnet itself: its settings, its OAuth apps, and — for an
2//! organization that has several — the tailnets in it.
3//!
4//! [`Error`] lives here too, for want of anywhere better. It is the shape
5//! every failing call answers with, and `ApiError::describe` is what reads it
6//! in practice; the model is here so the drift test covers it like any other
7//! schema.
8
9use crate::Secret;
10use crate::model;
11use crate::models::KnownValues;
12
13/// Which roles may accept an invitation to another tailnet.
14///
15/// `none` is one of the values rather than the field being absent, so leaving
16/// the setting off is itself a setting.
17pub const ROLES_ALLOWED_TO_JOIN: &[&str] = &["none", "admin", "member"];
18
19/// How the tailnet picks among subnet routers that advertise the same route.
20pub const ROUTE_SELECTIONS: &[&str] = &[
21    "active-passive-failover",
22    "regional-routing",
23    "regional-routing-failover",
24];
25
26pub const KNOWN_VALUES: &[KnownValues] = &[
27    (
28        "TailnetSettings.usersRoleAllowedToJoinExternalTailnets",
29        ROLES_ALLOWED_TO_JOIN,
30    ),
31    ("TailnetSettings.routeSelection", ROUTE_SELECTIONS),
32];
33
34model! {
35    /// What a failing call says went wrong.
36    Error {
37        message: "message" => String,
38    }
39
40    /// The tailnet-wide switches.
41    ///
42    /// Most are nullable in the description, where `null` means the tailnet's
43    /// plan does not carry the feature — which is not the same answer as
44    /// `false`, and is why they stay `Option` rather than defaulting.
45    TailnetSettings {
46        /// Stops the policy file being edited in the admin console, so that a
47        /// GitOps or Terraform workflow is the only writer.
48        acls_externally_managed_on: "aclsExternallyManagedOn" => bool,
49        /// Where the admin console points a reader when the above is on.
50        acls_external_link: "aclsExternalLink" => String,
51        devices_approval_on: "devicesApprovalOn" => bool,
52        devices_auto_updates_on: "devicesAutoUpdatesOn" => bool,
53        /// How long a device's key lasts before it must reauthenticate.
54        devices_key_duration_days: "devicesKeyDurationDays" => i64,
55        users_approval_on: "usersApprovalOn" => bool,
56        /// One of [`ROLES_ALLOWED_TO_JOIN`].
57        users_role_allowed_to_join_external_tailnets:
58            "usersRoleAllowedToJoinExternalTailnets" => String,
59        network_flow_logging_on: "networkFlowLoggingOn" => bool,
60        /// Read-only: `route_selection` is what a change sets, and a change
61        /// may not name both.
62        regional_routing_on: "regionalRoutingOn" => bool,
63        /// One of [`ROUTE_SELECTIONS`].
64        route_selection: "routeSelection" => String,
65        /// Whether posture integrations may collect device identity.
66        posture_identity_collection_on: "postureIdentityCollectionOn" => bool,
67        /// Whether devices can be issued HTTPS certificates.
68        https_enabled: "httpsEnabled" => bool,
69    }
70
71    /// An OAuth app, which is a third party a user can grant access to.
72    ///
73    /// Not to be confused with an OAuth client, which is a credential this
74    /// server can hold; see [`crate::credentials::Credentials`].
75    OAuthApp {
76        id: "id" => String,
77        /// 3 to 50 characters of `[A-Za-z0-9._-]`.
78        name: "name" => String,
79        /// At most 300 characters.
80        description: "description" => String,
81        /// Where the authorization code flow may return to. At least one is
82        /// required and each must be `https`.
83        redirect_uris: "redirectURIs" => Vec<String>,
84        /// Must be non-empty.
85        scopes: "scopes" => Vec<String>,
86        /// The device attributes this app may set.
87        allowed_node_attributes: "allowedNodeAttributes" => Vec<String>,
88        /// Sent when the app is created and never again.
89        client_secret: "clientSecret" => Secret,
90        created: "created" => String,
91        updated: "updated" => String,
92    }
93
94    /// Every OAuth app the tailnet has.
95    OAuthAppList as "GET /tailnet/{tailnet}/oauth-apps 200" {
96        oauth_apps: "oauthApps" => Vec<OAuthApp>,
97    }
98
99    /// What creating an OAuth app sends.
100    ///
101    /// The same five fields an update sends, and the description declares them
102    /// through the same shared schemas, so one struct covers both.
103    CreateOAuthAppRequest as "POST /tailnet/{tailnet}/oauth-apps body" {
104        /// 3 to 50 characters of `[A-Za-z0-9._-]`. Required.
105        name: "name" => String,
106        /// At most 300 characters.
107        description: "description" => String,
108        /// Required, at least one, each `https` — or `http` on localhost.
109        redirect_uris: "redirectURIs" => Vec<String>,
110        /// Required and non-empty, as `auth_keys:create` and the like.
111        scopes: "scopes" => Vec<String>,
112        /// Device attributes this app may set, each beginning `custom:`.
113        allowed_node_attributes: "allowedNodeAttributes" => Vec<String>,
114    }
115
116    /// What reconfiguring one sends, which is the same body. The secret is
117    /// neither regenerated nor returned.
118    UpdateOAuthAppRequest as "PUT /tailnet/{tailnet}/oauth-apps/{appId} body"
119        is CreateOAuthAppRequest;
120
121    /// One tailnet belonging to an organization.
122    OrganizationTailnet {
123        id: "id" => String,
124        display_name: "displayName" => String,
125        org_id: "orgId" => String,
126        created_at: "createdAt" => String,
127    }
128
129    /// A page of an organization's tailnets.
130    ListOrganizationTailnetsResponse {
131        tailnets: "tailnets" => Vec<OrganizationTailnet>,
132        /// Opaque, and the way to ask for the next page.
133        cursor: "cursor" => String,
134        /// Across every page, not this one.
135        total_count: "totalCount" => i64,
136    }
137
138    /// What creating a tailnet asks for.
139    CreateOrganizationTailnetRequest {
140        display_name: "displayName" => String,
141    }
142
143    /// An OAuth client scoped to a newly created tailnet, so that the caller
144    /// has a credential for it without a second round trip.
145    TailnetOAuthClient {
146        id: "id" => String,
147        /// Sent once, in the answer that created the tailnet.
148        secret: "secret" => Secret,
149    }
150
151    /// A newly created tailnet, or the one that already had the name.
152    CreateOrganizationTailnetResponse {
153        id: "id" => String,
154        display_name: "displayName" => String,
155        org_id: "orgId" => String,
156        /// The suffix this tailnet's MagicDNS names are built on.
157        dns_name: "dnsName" => String,
158        created_at: "createdAt" => String,
159        oauth_client: "oauthClient" => TailnetOAuthClient,
160        /// `true` where the call matched an existing tailnet rather than
161        /// making one, which is what makes creation safe to repeat.
162        already_exists: "alreadyExists" => bool,
163    }
164}