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}