@@ -113,6 +113,8 @@ func (f *Framework) HTTPClient() *http.Client {
113113 return f .httpClient
114114}
115115
116+ // CreateNamespace creates a uniquely-named namespace (prefixed with name and
117+ // a unix timestamp) and returns the actual name plus a cleanup function.
116118func (f * Framework ) CreateNamespace (ctx context.Context , name string , isClusterMonitoringNamespace bool ) (string , CleanupFunc , error ) {
117119 testNamespace := fmt .Sprintf ("%s-%d" , name , time .Now ().Unix ())
118120 namespace := & corev1.Namespace {
@@ -212,3 +214,102 @@ func createServiceAccountToken(clientset *kubernetes.Clientset) (string, error)
212214 }
213215 return resp .Status .Token , nil
214216}
217+ // ScopedUser represents a ServiceAccount with specific RBAC permissions for testing.
218+ type ScopedUser struct {
219+ Token string
220+ Cleanup CleanupFunc
221+ }
222+
223+ // requestServiceAccountToken creates a short-lived (1 hour) bearer token for
224+ // the named ServiceAccount via the TokenRequest API.
225+ func (f * Framework ) requestServiceAccountToken (ctx context.Context , namespace , name string ) (string , error ) {
226+ expSeconds := int64 (3600 )
227+ treq := & authv1.TokenRequest {
228+ Spec : authv1.TokenRequestSpec {ExpirationSeconds : & expSeconds },
229+ }
230+ tokenResp , err := f .Clientset .CoreV1 ().ServiceAccounts (namespace ).CreateToken (ctx , name , treq , metav1.CreateOptions {})
231+ if err != nil {
232+ return "" , fmt .Errorf ("requesting token for %s/%s: %w" , namespace , name , err )
233+ }
234+ return tokenResp .Status .Token , nil
235+ }
236+
237+ // CreateScopedUser creates a ServiceAccount in the given namespace with a Role
238+ // granting the specified verbs on the specified resources. Returns a bearer token
239+ // and a cleanup function. The apiGroup should be e.g. "monitoring.coreos.com".
240+ func (f * Framework ) CreateScopedUser (ctx context.Context , name , namespace , apiGroup string , resources , verbs []string ) (* ScopedUser , error ) {
241+ rollback := func () {
242+ _ = f .Clientset .RbacV1 ().RoleBindings (namespace ).Delete (ctx , name , metav1.DeleteOptions {})
243+ _ = f .Clientset .RbacV1 ().Roles (namespace ).Delete (ctx , name , metav1.DeleteOptions {})
244+ _ = f .Clientset .CoreV1 ().ServiceAccounts (namespace ).Delete (ctx , name , metav1.DeleteOptions {})
245+ }
246+
247+ sa := & corev1.ServiceAccount {
248+ ObjectMeta : metav1.ObjectMeta {Name : name },
249+ }
250+ if _ , err := f .Clientset .CoreV1 ().ServiceAccounts (namespace ).Create (ctx , sa , metav1.CreateOptions {}); err != nil && ! apierrors .IsAlreadyExists (err ) {
251+ return nil , fmt .Errorf ("creating service account %s/%s: %w" , namespace , name , err )
252+ }
253+
254+ role := & rbacv1.Role {
255+ ObjectMeta : metav1.ObjectMeta {Name : name },
256+ Rules : []rbacv1.PolicyRule {{
257+ APIGroups : []string {apiGroup },
258+ Resources : resources ,
259+ Verbs : verbs ,
260+ }},
261+ }
262+ if _ , err := f .Clientset .RbacV1 ().Roles (namespace ).Create (ctx , role , metav1.CreateOptions {}); err != nil && ! apierrors .IsAlreadyExists (err ) {
263+ rollback ()
264+ return nil , fmt .Errorf ("creating role %s/%s: %w" , namespace , name , err )
265+ }
266+
267+ rb := & rbacv1.RoleBinding {
268+ ObjectMeta : metav1.ObjectMeta {Name : name },
269+ Subjects : []rbacv1.Subject {{
270+ Kind : rbacv1 .ServiceAccountKind ,
271+ Name : name ,
272+ Namespace : namespace ,
273+ }},
274+ RoleRef : rbacv1.RoleRef {
275+ APIGroup : rbacv1 .GroupName ,
276+ Kind : "Role" ,
277+ Name : name ,
278+ },
279+ }
280+ if _ , err := f .Clientset .RbacV1 ().RoleBindings (namespace ).Create (ctx , rb , metav1.CreateOptions {}); err != nil && ! apierrors .IsAlreadyExists (err ) {
281+ rollback ()
282+ return nil , fmt .Errorf ("creating role binding %s/%s: %w" , namespace , name , err )
283+ }
284+
285+ token , err := f .requestServiceAccountToken (ctx , namespace , name )
286+ if err != nil {
287+ rollback ()
288+ return nil , err
289+ }
290+
291+ return & ScopedUser {Token : token , Cleanup : func () error { rollback (); return nil }}, nil
292+ }
293+
294+ // CreateUnprivilegedUser creates a ServiceAccount with no RBAC permissions.
295+ func (f * Framework ) CreateUnprivilegedUser (ctx context.Context , name , namespace string ) (* ScopedUser , error ) {
296+ sa := & corev1.ServiceAccount {
297+ ObjectMeta : metav1.ObjectMeta {Name : name },
298+ }
299+ if _ , err := f .Clientset .CoreV1 ().ServiceAccounts (namespace ).Create (ctx , sa , metav1.CreateOptions {}); err != nil && ! apierrors .IsAlreadyExists (err ) {
300+ return nil , fmt .Errorf ("creating service account %s/%s: %w" , namespace , name , err )
301+ }
302+
303+ token , err := f .requestServiceAccountToken (ctx , namespace , name )
304+ if err != nil {
305+ _ = f .Clientset .CoreV1 ().ServiceAccounts (namespace ).Delete (ctx , name , metav1.DeleteOptions {})
306+ return nil , err
307+ }
308+
309+ cleanup := func () error {
310+ _ = f .Clientset .CoreV1 ().ServiceAccounts (namespace ).Delete (ctx , name , metav1.DeleteOptions {})
311+ return nil
312+ }
313+
314+ return & ScopedUser {Token : token , Cleanup : cleanup }, nil
315+ }
0 commit comments