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