Skip to main content

tailscale_rest/models/
user.rs

1//! The people in a tailnet, invitations to join it, and who to email about it.
2
3use crate::model;
4use crate::models::KnownValues;
5
6/// The roles a user can hold.
7///
8/// `owner` is not in [`INVITE_ROLES`]: a tailnet has one, and it is
9/// transferred rather than invited.
10pub const USER_ROLES: &[&str] = &[
11    "owner",
12    "member",
13    "admin",
14    "it-admin",
15    "network-admin",
16    "billing-admin",
17    "auditor",
18];
19
20/// The roles an invitation can offer.
21pub const INVITE_ROLES: &[&str] = &[
22    "member",
23    "admin",
24    "it-admin",
25    "network-admin",
26    "billing-admin",
27    "auditor",
28];
29
30/// Whether a user belongs to this tailnet or is shared into it.
31pub const USER_TYPES: &[&str] = &["member", "shared"];
32
33/// Where a user stands with the tailnet.
34pub const USER_STATUSES: &[&str] = &[
35    "active",
36    "idle",
37    "suspended",
38    "needs-approval",
39    "over-billing-limit",
40];
41
42/// The three addresses a tailnet keeps.
43pub const CONTACT_TYPES: &[&str] = &["account", "support", "security"];
44
45/// [`USER_ROLES`] as a filter, where `all` means do not filter.
46pub const USER_ROLE_FILTERS: &[&str] = &[
47    "owner",
48    "member",
49    "admin",
50    "it-admin",
51    "network-admin",
52    "billing-admin",
53    "auditor",
54    "all",
55];
56
57/// [`USER_TYPES`] as a filter, with the same `all`.
58pub const USER_TYPE_FILTERS: &[&str] = &["member", "shared", "all"];
59
60pub const KNOWN_VALUES: &[KnownValues] = &[
61    ("User.role", USER_ROLES),
62    ("User.type", USER_TYPES),
63    ("User.status", USER_STATUSES),
64    ("UserInvite.role", INVITE_ROLES),
65    ("?contactType", CONTACT_TYPES),
66    ("/tailnet/{tailnet}/users ?role", USER_ROLE_FILTERS),
67    ("/tailnet/{tailnet}/users ?type", USER_TYPE_FILTERS),
68    ("POST /users/{userId}/role body.role", USER_ROLES),
69    ("POST /tailnet/{tailnet}/user-invites body[].role", INVITE_ROLES),
70];
71
72model! {
73    /// A person with access to the tailnet.
74    User {
75        id: "id" => String,
76        display_name: "displayName" => String,
77        login_name: "loginName" => String,
78        profile_pic_url: "profilePicUrl" => String,
79        tailnet_id: "tailnetId" => String,
80        created: "created" => String,
81        /// One of [`USER_TYPES`].
82        user_type: "type" => String,
83        /// One of [`USER_ROLES`].
84        role: "role" => String,
85        /// One of [`USER_STATUSES`].
86        status: "status" => String,
87        device_count: "deviceCount" => i64,
88        last_seen: "lastSeen" => String,
89        currently_connected: "currentlyConnected" => bool,
90    }
91
92    /// An invitation for someone outside the tailnet to join it.
93    UserInvite {
94        id: "id" => String,
95        /// The role the invitee gets on accepting. One of [`INVITE_ROLES`].
96        role: "role" => String,
97        tailnet_id: "tailnetId" => i64,
98        inviter_id: "inviterId" => i64,
99        /// Empty for an invite nobody was mailed, whose URL is shared by hand.
100        email: "email" => String,
101        last_email_sent_at: "lastEmailSentAt" => String,
102        /// Anyone holding this link can accept, not only the addressee.
103        invite_url: "inviteUrl" => String,
104    }
105
106    /// An address the tailnet's notices go to.
107    ///
108    /// There is one of these per contact kind — account, support, security —
109    /// and the kind is in the path rather than in the body.
110    Contact {
111        /// The address in use, which only changes once the new one is verified.
112        email: "email" => String,
113        /// A newly set address that has not been verified yet.
114        fallback_email: "fallbackEmail" => String,
115        needs_verification: "needsVerification" => bool,
116    }
117
118    // -----------------------------------------------------------------------
119    // The shapes the routes carry.
120    // -----------------------------------------------------------------------
121
122    /// The tailnet's users, filtered by whatever the query asked for.
123    UserList as "GET /tailnet/{tailnet}/users 200" {
124        users: "users" => Vec<User>,
125    }
126
127    /// What changing somebody's role sends.
128    UserRole as "POST /users/{userId}/role body" {
129        /// One of [`USER_ROLES`]. A user-owned credential cannot change its
130        /// own holder's role.
131        role: "role" => String,
132    }
133
134    /// One invitation to create, as the request's array carries them.
135    ///
136    /// A create takes a list of these, so several people can be invited in one
137    /// call and each gets its own role.
138    CreateUserInvite as "POST /tailnet/{tailnet}/user-invites body[]" {
139        /// One of [`INVITE_ROLES`], which has no `owner` in it.
140        role: "role" => String,
141        /// Omit to create an invite nobody is mailed, whose `inviteUrl` is
142        /// then shared by hand.
143        email: "email" => String,
144    }
145
146    /// All three contacts at once, keyed by kind.
147    ///
148    /// Not a map, because the kinds are fixed: an answer with a fourth key
149    /// would be the description having changed rather than a tailnet having
150    /// more contacts.
151    Contacts as "GET /tailnet/{tailnet}/contacts 200" {
152        account: "account" => Contact,
153        support: "support" => Contact,
154        security: "security" => Contact,
155    }
156
157    /// What changing one contact sends. The kind is in the path.
158    UpdateContact as "PATCH /tailnet/{tailnet}/contacts/{contactType} body" {
159        /// Setting this mails a verification link; until it is followed the
160        /// old address stays in use and this one is the `fallbackEmail`.
161        email: "email" => String,
162    }
163}