Skip to content

Commit a116dc6

Browse files
mathstufkwrobot
authored andcommitted
Merge topic 'pat-create-endpoint-fixes'
a9f4be2 api/personal_access_tokens: fix creation endpoint scopes Acked-by: Kitware Robot <kwrobot@kitware.com> Tested-by: buildbot <buildbot@kitware.com> Acked-by: Brad King <brad.king@kitware.com> Merge-request: !470
2 parents 2f15005 + a9f4be2 commit a116dc6

File tree

4 files changed

+121
-73
lines changed

4 files changed

+121
-73
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@
55
* Actually build the
66
`/project/:project/issues/:issue_iid/related_merge_requests` endpoint.
77

8+
## Breaking changes
9+
10+
* `api::users::CreatePersonalAccessToken` now uses
11+
`PersonalAccessTokenCreateScope` as this endpoint is actually quite limited
12+
compared to the `CreatePersonalAccessTokenForUser` endpoint.
13+
814
# v0.1611.1
915

1016
## Additions

src/api/users/personal_access_tokens.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@ mod create_for_user;
1616
pub use self::create::CreatePersonalAccessToken;
1717
pub use self::create::CreatePersonalAccessTokenBuilder;
1818
pub use self::create::CreatePersonalAccessTokenBuilderError;
19-
pub use self::create::PersonalAccessTokenScope;
19+
pub use self::create::PersonalAccessTokenCreateScope;
2020

2121
pub use self::create_for_user::CreatePersonalAccessTokenForUser;
2222
pub use self::create_for_user::CreatePersonalAccessTokenForUserBuilder;
2323
pub use self::create_for_user::CreatePersonalAccessTokenForUserBuilderError;
24+
pub use self::create_for_user::PersonalAccessTokenScope;

src/api/users/personal_access_tokens/create.rs

Lines changed: 16 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -15,57 +15,21 @@ use crate::api::ParamValue;
1515
/// Scopes for personal access tokens.
1616
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
1717
#[non_exhaustive]
18-
pub enum PersonalAccessTokenScope {
19-
/// Access the API and perform git reads and writes.
20-
Api,
21-
/// Access to read the user information.
22-
ReadUser,
23-
/// Access read-only API endpoints.
24-
ReadApi,
25-
/// Read access to repositories.
26-
ReadRepository,
27-
/// Write access to repositories.
28-
WriteRepository,
29-
/// Read access to Docker registries.
30-
ReadRegistry,
31-
/// Write access to Docker registries.
32-
WriteRegistry,
33-
/// Permission to `sudo` as other users (administrator only).
34-
Sudo,
35-
/// Permission to access administrator API actions.
36-
AdminMode,
37-
/// Permission to create instance runners.
38-
CreateRunner,
39-
/// Access to AI features (GitLab Duo for JetBrains).
40-
AiFeatures,
18+
pub enum PersonalAccessTokenCreateScope {
4119
/// Access to perform Kubernetes API calls.
4220
K8sProxy,
43-
/// Access to the Service Ping payload.
44-
ReadServicePing,
4521
}
4622

47-
impl PersonalAccessTokenScope {
23+
impl PersonalAccessTokenCreateScope {
4824
/// The scope as a query parameter.
4925
pub(crate) fn as_str(self) -> &'static str {
5026
match self {
51-
Self::Api => "api",
52-
Self::ReadUser => "read_user",
53-
Self::ReadApi => "read_api",
54-
Self::ReadRepository => "read_repository",
55-
Self::WriteRepository => "write_repository",
56-
Self::ReadRegistry => "read_registry",
57-
Self::WriteRegistry => "write_registry",
58-
Self::Sudo => "sudo",
59-
Self::AdminMode => "admin_mode",
60-
Self::CreateRunner => "create_runner",
61-
Self::AiFeatures => "ai_features",
6227
Self::K8sProxy => "k8s_proxy",
63-
Self::ReadServicePing => "read_service_ping",
6428
}
6529
}
6630
}
6731

68-
impl ParamValue<'static> for PersonalAccessTokenScope {
32+
impl ParamValue<'static> for PersonalAccessTokenCreateScope {
6933
fn as_value(&self) -> Cow<'static, str> {
7034
self.as_str().into()
7135
}
@@ -80,7 +44,7 @@ pub struct CreatePersonalAccessToken<'a> {
8044
name: Cow<'a, str>,
8145
/// The scopes to allow the token to access.
8246
#[builder(setter(name = "_scopes"), private)]
83-
scopes: BTreeSet<PersonalAccessTokenScope>,
47+
scopes: BTreeSet<PersonalAccessTokenCreateScope>,
8448

8549
/// When the token expires.
8650
#[builder(default)]
@@ -96,15 +60,15 @@ impl<'a> CreatePersonalAccessToken<'a> {
9660

9761
impl<'a> CreatePersonalAccessTokenBuilder<'a> {
9862
/// Add a scope for the token.
99-
pub fn scope(&mut self, scope: PersonalAccessTokenScope) -> &mut Self {
63+
pub fn scope(&mut self, scope: PersonalAccessTokenCreateScope) -> &mut Self {
10064
self.scopes.get_or_insert_with(BTreeSet::new).insert(scope);
10165
self
10266
}
10367

10468
/// Add scopes for the token.
10569
pub fn scopes<I>(&mut self, scopes: I) -> &mut Self
10670
where
107-
I: Iterator<Item = PersonalAccessTokenScope>,
71+
I: Iterator<Item = PersonalAccessTokenCreateScope>,
10872
{
10973
self.scopes.get_or_insert_with(BTreeSet::new).extend(scopes);
11074
self
@@ -139,34 +103,15 @@ mod tests {
139103
use http::Method;
140104

141105
use crate::api::users::personal_access_tokens::{
142-
CreatePersonalAccessToken, CreatePersonalAccessTokenBuilderError, PersonalAccessTokenScope,
106+
CreatePersonalAccessToken, CreatePersonalAccessTokenBuilderError,
107+
PersonalAccessTokenCreateScope,
143108
};
144109
use crate::api::{self, Query};
145110
use crate::test::client::{ExpectedUrl, SingleTestClient};
146111

147112
#[test]
148-
fn personal_access_token_scope_as_str() {
149-
let items = &[
150-
(PersonalAccessTokenScope::Api, "api"),
151-
(PersonalAccessTokenScope::ReadUser, "read_user"),
152-
(PersonalAccessTokenScope::ReadApi, "read_api"),
153-
(PersonalAccessTokenScope::ReadRepository, "read_repository"),
154-
(
155-
PersonalAccessTokenScope::WriteRepository,
156-
"write_repository",
157-
),
158-
(PersonalAccessTokenScope::ReadRegistry, "read_registry"),
159-
(PersonalAccessTokenScope::WriteRegistry, "write_registry"),
160-
(PersonalAccessTokenScope::Sudo, "sudo"),
161-
(PersonalAccessTokenScope::AdminMode, "admin_mode"),
162-
(PersonalAccessTokenScope::CreateRunner, "create_runner"),
163-
(PersonalAccessTokenScope::AiFeatures, "ai_features"),
164-
(PersonalAccessTokenScope::K8sProxy, "k8s_proxy"),
165-
(
166-
PersonalAccessTokenScope::ReadServicePing,
167-
"read_service_ping",
168-
),
169-
];
113+
fn personal_access_token_create_scope_as_str() {
114+
let items = &[(PersonalAccessTokenCreateScope::K8sProxy, "k8s_proxy")];
170115

171116
for (i, s) in items {
172117
assert_eq!(i.as_str(), *s);
@@ -182,7 +127,7 @@ mod tests {
182127
#[test]
183128
fn name_is_necessary() {
184129
let err = CreatePersonalAccessToken::builder()
185-
.scope(PersonalAccessTokenScope::Api)
130+
.scope(PersonalAccessTokenCreateScope::K8sProxy)
186131
.build()
187132
.unwrap_err();
188133
crate::test::assert_missing_field!(err, CreatePersonalAccessTokenBuilderError, "name");
@@ -201,7 +146,7 @@ mod tests {
201146
fn user_name_and_scopes_are_sufficient() {
202147
CreatePersonalAccessToken::builder()
203148
.name("name")
204-
.scope(PersonalAccessTokenScope::ReadUser)
149+
.scope(PersonalAccessTokenCreateScope::K8sProxy)
205150
.build()
206151
.unwrap();
207152
}
@@ -212,14 +157,14 @@ mod tests {
212157
.method(Method::POST)
213158
.endpoint("users/personal_access_tokens")
214159
.content_type("application/x-www-form-urlencoded")
215-
.body_str(concat!("name=name", "&scopes%5B%5D=api"))
160+
.body_str(concat!("name=name", "&scopes%5B%5D=k8s_proxy"))
216161
.build()
217162
.unwrap();
218163
let client = SingleTestClient::new_raw(endpoint, "");
219164

220165
let endpoint = CreatePersonalAccessToken::builder()
221166
.name("name")
222-
.scopes([PersonalAccessTokenScope::Api].iter().cloned())
167+
.scopes([PersonalAccessTokenCreateScope::K8sProxy].iter().cloned())
223168
.build()
224169
.unwrap();
225170
api::ignore(endpoint).query(&client).unwrap();
@@ -234,15 +179,15 @@ mod tests {
234179
.body_str(concat!(
235180
"name=name",
236181
"&expires_at=2022-01-01",
237-
"&scopes%5B%5D=api",
182+
"&scopes%5B%5D=k8s_proxy",
238183
))
239184
.build()
240185
.unwrap();
241186
let client = SingleTestClient::new_raw(endpoint, "");
242187

243188
let endpoint = CreatePersonalAccessToken::builder()
244189
.name("name")
245-
.scope(PersonalAccessTokenScope::Api)
190+
.scope(PersonalAccessTokenCreateScope::K8sProxy)
246191
.expires_at(NaiveDate::from_ymd_opt(2022, 1, 1).unwrap())
247192
.build()
248193
.unwrap();

src/api/users/personal_access_tokens/create_for_user.rs

Lines changed: 97 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,74 @@ use chrono::NaiveDate;
1010
use derive_builder::Builder;
1111

1212
use crate::api::endpoint_prelude::*;
13-
use crate::api::users::personal_access_tokens::PersonalAccessTokenScope;
13+
use crate::api::ParamValue;
14+
15+
/// Scopes for personal access tokens.
16+
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
17+
#[non_exhaustive]
18+
pub enum PersonalAccessTokenScope {
19+
/// Access the API and perform git reads and writes.
20+
Api,
21+
/// Access to read the user information.
22+
ReadUser,
23+
/// Access read-only API endpoints.
24+
ReadApi,
25+
/// Read access to repositories.
26+
ReadRepository,
27+
/// Write access to repositories.
28+
WriteRepository,
29+
/// Read access to Docker registries.
30+
ReadRegistry,
31+
/// Write access to Docker registries.
32+
WriteRegistry,
33+
/// Permission to `sudo` as other users (administrator only).
34+
Sudo,
35+
/// Permission to access administrator API actions.
36+
AdminMode,
37+
/// Permission to create instance runners.
38+
CreateRunner,
39+
/// Access to AI features (GitLab Duo for JetBrains).
40+
AiFeatures,
41+
/// Access to perform Kubernetes API calls.
42+
K8sProxy,
43+
/// Access to the Service Ping payload.
44+
ReadServicePing,
45+
}
46+
47+
impl PersonalAccessTokenScope {
48+
/// The scope as a query parameter.
49+
pub(crate) fn as_str(self) -> &'static str {
50+
match self {
51+
Self::Api => "api",
52+
Self::ReadUser => "read_user",
53+
Self::ReadApi => "read_api",
54+
Self::ReadRepository => "read_repository",
55+
Self::WriteRepository => "write_repository",
56+
Self::ReadRegistry => "read_registry",
57+
Self::WriteRegistry => "write_registry",
58+
Self::Sudo => "sudo",
59+
Self::AdminMode => "admin_mode",
60+
Self::CreateRunner => "create_runner",
61+
Self::AiFeatures => "ai_features",
62+
Self::K8sProxy => "k8s_proxy",
63+
Self::ReadServicePing => "read_service_ping",
64+
}
65+
}
66+
67+
/// Transform into a `create` scope if possible.
68+
pub fn as_create_scope(self) -> Option<super::PersonalAccessTokenCreateScope> {
69+
match self {
70+
Self::K8sProxy => Some(super::PersonalAccessTokenCreateScope::K8sProxy),
71+
_ => None,
72+
}
73+
}
74+
}
75+
76+
impl ParamValue<'static> for PersonalAccessTokenScope {
77+
fn as_value(&self) -> Cow<'static, str> {
78+
self.as_str().into()
79+
}
80+
}
1481

1582
/// Create a new personal access token for a user.
1683
#[derive(Debug, Builder, Clone)]
@@ -88,6 +155,35 @@ mod tests {
88155
use crate::api::{self, Query};
89156
use crate::test::client::{ExpectedUrl, SingleTestClient};
90157

158+
#[test]
159+
fn personal_access_token_scope_as_str() {
160+
let items = &[
161+
(PersonalAccessTokenScope::Api, "api"),
162+
(PersonalAccessTokenScope::ReadUser, "read_user"),
163+
(PersonalAccessTokenScope::ReadApi, "read_api"),
164+
(PersonalAccessTokenScope::ReadRepository, "read_repository"),
165+
(
166+
PersonalAccessTokenScope::WriteRepository,
167+
"write_repository",
168+
),
169+
(PersonalAccessTokenScope::ReadRegistry, "read_registry"),
170+
(PersonalAccessTokenScope::WriteRegistry, "write_registry"),
171+
(PersonalAccessTokenScope::Sudo, "sudo"),
172+
(PersonalAccessTokenScope::AdminMode, "admin_mode"),
173+
(PersonalAccessTokenScope::CreateRunner, "create_runner"),
174+
(PersonalAccessTokenScope::AiFeatures, "ai_features"),
175+
(PersonalAccessTokenScope::K8sProxy, "k8s_proxy"),
176+
(
177+
PersonalAccessTokenScope::ReadServicePing,
178+
"read_service_ping",
179+
),
180+
];
181+
182+
for (i, s) in items {
183+
assert_eq!(i.as_str(), *s);
184+
}
185+
}
186+
91187
#[test]
92188
fn user_name_and_scopes_are_necessary() {
93189
let err = CreatePersonalAccessTokenForUser::builder()

0 commit comments

Comments
 (0)