github.com/hernad/nomad@v1.6.112/command/agent/acl_endpoint_test.go (about)

     1  // Copyright (c) HashiCorp, Inc.
     2  // SPDX-License-Identifier: MPL-2.0
     3  
     4  package agent
     5  
     6  import (
     7  	"bytes"
     8  	"fmt"
     9  	"net/http"
    10  	"net/http/httptest"
    11  	"net/url"
    12  	"testing"
    13  	"time"
    14  
    15  	"github.com/golang-jwt/jwt/v5"
    16  	capOIDC "github.com/hashicorp/cap/oidc"
    17  	"github.com/hernad/nomad/ci"
    18  	"github.com/hernad/nomad/helper/uuid"
    19  	"github.com/hernad/nomad/nomad/mock"
    20  	"github.com/hernad/nomad/nomad/structs"
    21  	"github.com/shoenig/test/must"
    22  	"github.com/stretchr/testify/assert"
    23  	"github.com/stretchr/testify/require"
    24  )
    25  
    26  func TestHTTP_ACLPolicyList(t *testing.T) {
    27  	ci.Parallel(t)
    28  	httpACLTest(t, nil, func(s *TestAgent) {
    29  		p1 := mock.ACLPolicy()
    30  		p2 := mock.ACLPolicy()
    31  		p3 := mock.ACLPolicy()
    32  		args := structs.ACLPolicyUpsertRequest{
    33  			Policies: []*structs.ACLPolicy{p1, p2, p3},
    34  			WriteRequest: structs.WriteRequest{
    35  				Region:    "global",
    36  				AuthToken: s.RootToken.SecretID,
    37  			},
    38  		}
    39  		var resp structs.GenericResponse
    40  		if err := s.Agent.RPC("ACL.UpsertPolicies", &args, &resp); err != nil {
    41  			t.Fatalf("err: %v", err)
    42  		}
    43  
    44  		// Make the HTTP request
    45  		req, err := http.NewRequest("GET", "/v1/acl/policies", nil)
    46  		if err != nil {
    47  			t.Fatalf("err: %v", err)
    48  		}
    49  		respW := httptest.NewRecorder()
    50  		setToken(req, s.RootToken)
    51  
    52  		// Make the request
    53  		obj, err := s.Server.ACLPoliciesRequest(respW, req)
    54  		if err != nil {
    55  			t.Fatalf("err: %v", err)
    56  		}
    57  
    58  		// Check for the index
    59  		if respW.Result().Header.Get("X-Nomad-Index") == "" {
    60  			t.Fatalf("missing index")
    61  		}
    62  		if respW.Result().Header.Get("X-Nomad-KnownLeader") != "true" {
    63  			t.Fatalf("missing known leader")
    64  		}
    65  		if respW.Result().Header.Get("X-Nomad-LastContact") == "" {
    66  			t.Fatalf("missing last contact")
    67  		}
    68  
    69  		// Check the output
    70  		n := obj.([]*structs.ACLPolicyListStub)
    71  		if len(n) != 3 {
    72  			t.Fatalf("bad: %#v", n)
    73  		}
    74  	})
    75  }
    76  
    77  func TestHTTP_ACLPolicyQuery(t *testing.T) {
    78  	ci.Parallel(t)
    79  	httpACLTest(t, nil, func(s *TestAgent) {
    80  		p1 := mock.ACLPolicy()
    81  		args := structs.ACLPolicyUpsertRequest{
    82  			Policies: []*structs.ACLPolicy{p1},
    83  			WriteRequest: structs.WriteRequest{
    84  				Region:    "global",
    85  				AuthToken: s.RootToken.SecretID,
    86  			},
    87  		}
    88  		var resp structs.GenericResponse
    89  		if err := s.Agent.RPC("ACL.UpsertPolicies", &args, &resp); err != nil {
    90  			t.Fatalf("err: %v", err)
    91  		}
    92  
    93  		// Make the HTTP request
    94  		req, err := http.NewRequest("GET", "/v1/acl/policy/"+p1.Name, nil)
    95  		if err != nil {
    96  			t.Fatalf("err: %v", err)
    97  		}
    98  		respW := httptest.NewRecorder()
    99  		setToken(req, s.RootToken)
   100  
   101  		// Make the request
   102  		obj, err := s.Server.ACLPolicySpecificRequest(respW, req)
   103  		if err != nil {
   104  			t.Fatalf("err: %v", err)
   105  		}
   106  
   107  		// Check for the index
   108  		if respW.Result().Header.Get("X-Nomad-Index") == "" {
   109  			t.Fatalf("missing index")
   110  		}
   111  		if respW.Result().Header.Get("X-Nomad-KnownLeader") != "true" {
   112  			t.Fatalf("missing known leader")
   113  		}
   114  		if respW.Result().Header.Get("X-Nomad-LastContact") == "" {
   115  			t.Fatalf("missing last contact")
   116  		}
   117  
   118  		// Check the output
   119  		n := obj.(*structs.ACLPolicy)
   120  		if n.Name != p1.Name {
   121  			t.Fatalf("bad: %#v", n)
   122  		}
   123  	})
   124  }
   125  
   126  func TestHTTP_ACLPolicyCreate(t *testing.T) {
   127  	ci.Parallel(t)
   128  	httpACLTest(t, nil, func(s *TestAgent) {
   129  		// Make the HTTP request
   130  		p1 := mock.ACLPolicy()
   131  		buf := encodeReq(p1)
   132  		req, err := http.NewRequest("PUT", "/v1/acl/policy/"+p1.Name, buf)
   133  		must.NoError(t, err)
   134  
   135  		respW := httptest.NewRecorder()
   136  		setToken(req, s.RootToken)
   137  
   138  		// Make the request
   139  		obj, err := s.Server.ACLPolicySpecificRequest(respW, req)
   140  		must.NoError(t, err)
   141  		must.Nil(t, obj)
   142  
   143  		// Check for the index
   144  		must.StrNotEqFold(t, "", respW.Result().Header.Get("X-Nomad-Index"))
   145  
   146  		// Check policy was created
   147  		state := s.Agent.server.State()
   148  		out, err := state.ACLPolicyByName(nil, p1.Name)
   149  		must.NoError(t, err)
   150  		must.NotNil(t, out)
   151  
   152  		p1.CreateIndex, p1.ModifyIndex = out.CreateIndex, out.ModifyIndex
   153  		must.Eq(t, p1.Name, out.Name)
   154  		must.Eq(t, p1, out)
   155  
   156  		// Create a policy that is invalid. This ensures we call the validation
   157  		// func in the RPC handler, also that the correct code and error is
   158  		// returned.
   159  		aclPolicy2 := mock.ACLPolicy()
   160  		aclPolicy2.Rules = "invalid"
   161  
   162  		aclPolicy2Req, err := http.NewRequest(http.MethodPut, "/v1/acl/policy/"+aclPolicy2.Name, encodeReq(aclPolicy2))
   163  		must.NoError(t, err)
   164  
   165  		respW = httptest.NewRecorder()
   166  		setToken(aclPolicy2Req, s.RootToken)
   167  
   168  		// Make the request
   169  		aclPolicy2Obj, err := s.Server.ACLPolicySpecificRequest(respW, aclPolicy2Req)
   170  		must.ErrorContains(t, err, "400")
   171  		must.ErrorContains(t, err, "failed to parse rules")
   172  		must.Nil(t, aclPolicy2Obj)
   173  	})
   174  }
   175  
   176  func TestHTTP_ACLPolicyDelete(t *testing.T) {
   177  	ci.Parallel(t)
   178  	httpACLTest(t, nil, func(s *TestAgent) {
   179  		p1 := mock.ACLPolicy()
   180  		args := structs.ACLPolicyUpsertRequest{
   181  			Policies: []*structs.ACLPolicy{p1},
   182  			WriteRequest: structs.WriteRequest{
   183  				Region:    "global",
   184  				AuthToken: s.RootToken.SecretID,
   185  			},
   186  		}
   187  		var resp structs.GenericResponse
   188  		if err := s.Agent.RPC("ACL.UpsertPolicies", &args, &resp); err != nil {
   189  			t.Fatalf("err: %v", err)
   190  		}
   191  
   192  		// Make the HTTP request
   193  		req, err := http.NewRequest("DELETE", "/v1/acl/policy/"+p1.Name, nil)
   194  		if err != nil {
   195  			t.Fatalf("err: %v", err)
   196  		}
   197  		respW := httptest.NewRecorder()
   198  		setToken(req, s.RootToken)
   199  
   200  		// Make the request
   201  		obj, err := s.Server.ACLPolicySpecificRequest(respW, req)
   202  		assert.Nil(t, err)
   203  		assert.Nil(t, obj)
   204  
   205  		// Check for the index
   206  		if respW.Result().Header.Get("X-Nomad-Index") == "" {
   207  			t.Fatalf("missing index")
   208  		}
   209  
   210  		// Check policy was created
   211  		state := s.Agent.server.State()
   212  		out, err := state.ACLPolicyByName(nil, p1.Name)
   213  		assert.Nil(t, err)
   214  		assert.Nil(t, out)
   215  	})
   216  }
   217  
   218  func TestHTTP_ACLTokenBootstrap(t *testing.T) {
   219  	ci.Parallel(t)
   220  	conf := func(c *Config) {
   221  		c.ACL.Enabled = true
   222  		c.ACL.PolicyTTL = 0 // Special flag to disable auto-bootstrap
   223  	}
   224  	httpTest(t, conf, func(s *TestAgent) {
   225  		// Make the HTTP request
   226  		req, err := http.NewRequest("PUT", "/v1/acl/bootstrap", nil)
   227  		if err != nil {
   228  			t.Fatalf("err: %v", err)
   229  		}
   230  		respW := httptest.NewRecorder()
   231  
   232  		// Make the request
   233  		obj, err := s.Server.ACLTokenBootstrap(respW, req)
   234  		if err != nil {
   235  			t.Fatalf("err: %v", err)
   236  		}
   237  
   238  		// Check for the index
   239  		if respW.Result().Header.Get("X-Nomad-Index") == "" {
   240  			t.Fatalf("missing index")
   241  		}
   242  
   243  		// Check the output
   244  		n := obj.(*structs.ACLToken)
   245  		assert.NotNil(t, n)
   246  		assert.Equal(t, "Bootstrap Token", n.Name)
   247  	})
   248  }
   249  
   250  func TestHTTP_ACLTokenBootstrapOperator(t *testing.T) {
   251  	ci.Parallel(t)
   252  	conf := func(c *Config) {
   253  		c.ACL.Enabled = true
   254  		c.ACL.PolicyTTL = 0 // Special flag to disable auto-bootstrap
   255  	}
   256  	httpTest(t, conf, func(s *TestAgent) {
   257  		// Provide token
   258  		args := structs.ACLTokenBootstrapRequest{
   259  			BootstrapSecret: "2b778dd9-f5f1-6f29-b4b4-9a5fa948757a",
   260  		}
   261  
   262  		buf := encodeReq(args)
   263  
   264  		// Make the HTTP request
   265  		req, err := http.NewRequest("PUT", "/v1/acl/bootstrap", buf)
   266  		if err != nil {
   267  			t.Fatalf("err: %v", err)
   268  		}
   269  
   270  		// Since we're not actually writing this HTTP request, we have
   271  		// to manually set ContentLength
   272  		req.ContentLength = -1
   273  
   274  		respW := httptest.NewRecorder()
   275  		// Make the request
   276  		obj, err := s.Server.ACLTokenBootstrap(respW, req)
   277  		if err != nil {
   278  			t.Fatalf("err: %v", err)
   279  		}
   280  
   281  		// Check for the index
   282  		if respW.Result().Header.Get("X-Nomad-Index") == "" {
   283  			t.Fatalf("missing index")
   284  		}
   285  
   286  		// Check the output
   287  		n := obj.(*structs.ACLToken)
   288  		assert.NotNil(t, n)
   289  		assert.Equal(t, args.BootstrapSecret, n.SecretID)
   290  	})
   291  }
   292  
   293  func TestHTTP_ACLTokenList(t *testing.T) {
   294  	ci.Parallel(t)
   295  	httpACLTest(t, nil, func(s *TestAgent) {
   296  		p1 := mock.ACLToken()
   297  		p1.AccessorID = ""
   298  		p2 := mock.ACLToken()
   299  		p2.AccessorID = ""
   300  		p3 := mock.ACLToken()
   301  		p3.AccessorID = ""
   302  		args := structs.ACLTokenUpsertRequest{
   303  			Tokens: []*structs.ACLToken{p1, p2, p3},
   304  			WriteRequest: structs.WriteRequest{
   305  				Region:    "global",
   306  				AuthToken: s.RootToken.SecretID,
   307  			},
   308  		}
   309  		var resp structs.ACLTokenUpsertResponse
   310  		if err := s.Agent.RPC("ACL.UpsertTokens", &args, &resp); err != nil {
   311  			t.Fatalf("err: %v", err)
   312  		}
   313  
   314  		// Make the HTTP request
   315  		req, err := http.NewRequest("GET", "/v1/acl/tokens", nil)
   316  		if err != nil {
   317  			t.Fatalf("err: %v", err)
   318  		}
   319  		respW := httptest.NewRecorder()
   320  		setToken(req, s.RootToken)
   321  
   322  		// Make the request
   323  		obj, err := s.Server.ACLTokensRequest(respW, req)
   324  		if err != nil {
   325  			t.Fatalf("err: %v", err)
   326  		}
   327  
   328  		// Check for the index
   329  		if respW.Result().Header.Get("X-Nomad-Index") == "" {
   330  			t.Fatalf("missing index")
   331  		}
   332  		if respW.Result().Header.Get("X-Nomad-KnownLeader") != "true" {
   333  			t.Fatalf("missing known leader")
   334  		}
   335  		if respW.Result().Header.Get("X-Nomad-LastContact") == "" {
   336  			t.Fatalf("missing last contact")
   337  		}
   338  
   339  		// Check the output (includes bootstrap token)
   340  		n := obj.([]*structs.ACLTokenListStub)
   341  		if len(n) != 4 {
   342  			t.Fatalf("bad: %#v", n)
   343  		}
   344  	})
   345  }
   346  
   347  func TestHTTP_ACLTokenQuery(t *testing.T) {
   348  	ci.Parallel(t)
   349  	httpACLTest(t, nil, func(s *TestAgent) {
   350  		p1 := mock.ACLToken()
   351  		p1.AccessorID = ""
   352  		args := structs.ACLTokenUpsertRequest{
   353  			Tokens: []*structs.ACLToken{p1},
   354  			WriteRequest: structs.WriteRequest{
   355  				Region:    "global",
   356  				AuthToken: s.RootToken.SecretID,
   357  			},
   358  		}
   359  		var resp structs.ACLTokenUpsertResponse
   360  		if err := s.Agent.RPC("ACL.UpsertTokens", &args, &resp); err != nil {
   361  			t.Fatalf("err: %v", err)
   362  		}
   363  		out := resp.Tokens[0]
   364  
   365  		// Make the HTTP request
   366  		req, err := http.NewRequest("GET", "/v1/acl/token/"+out.AccessorID, nil)
   367  		if err != nil {
   368  			t.Fatalf("err: %v", err)
   369  		}
   370  		respW := httptest.NewRecorder()
   371  		setToken(req, s.RootToken)
   372  
   373  		// Make the request
   374  		obj, err := s.Server.ACLTokenSpecificRequest(respW, req)
   375  		if err != nil {
   376  			t.Fatalf("err: %v", err)
   377  		}
   378  
   379  		// Check for the index
   380  		if respW.Result().Header.Get("X-Nomad-Index") == "" {
   381  			t.Fatalf("missing index")
   382  		}
   383  		if respW.Result().Header.Get("X-Nomad-KnownLeader") != "true" {
   384  			t.Fatalf("missing known leader")
   385  		}
   386  		if respW.Result().Header.Get("X-Nomad-LastContact") == "" {
   387  			t.Fatalf("missing last contact")
   388  		}
   389  
   390  		// Check the output
   391  		n := obj.(*structs.ACLToken)
   392  		assert.Equal(t, out, n)
   393  	})
   394  }
   395  
   396  func TestHTTP_ACLTokenSelf(t *testing.T) {
   397  	ci.Parallel(t)
   398  	httpACLTest(t, nil, func(s *TestAgent) {
   399  		p1 := mock.ACLToken()
   400  		p1.AccessorID = ""
   401  		args := structs.ACLTokenUpsertRequest{
   402  			Tokens: []*structs.ACLToken{p1},
   403  			WriteRequest: structs.WriteRequest{
   404  				Region:    "global",
   405  				AuthToken: s.RootToken.SecretID,
   406  			},
   407  		}
   408  		var resp structs.ACLTokenUpsertResponse
   409  		if err := s.Agent.RPC("ACL.UpsertTokens", &args, &resp); err != nil {
   410  			t.Fatalf("err: %v", err)
   411  		}
   412  		out := resp.Tokens[0]
   413  
   414  		// Make the HTTP request
   415  		req, err := http.NewRequest("GET", "/v1/acl/token/self", nil)
   416  		if err != nil {
   417  			t.Fatalf("err: %v", err)
   418  		}
   419  		respW := httptest.NewRecorder()
   420  		setToken(req, out)
   421  
   422  		// Make the request
   423  		obj, err := s.Server.ACLTokenSpecificRequest(respW, req)
   424  		if err != nil {
   425  			t.Fatalf("err: %v", err)
   426  		}
   427  
   428  		// Check for the index
   429  		if respW.Result().Header.Get("X-Nomad-Index") == "" {
   430  			t.Fatalf("missing index")
   431  		}
   432  		if respW.Result().Header.Get("X-Nomad-KnownLeader") != "true" {
   433  			t.Fatalf("missing known leader")
   434  		}
   435  		if respW.Result().Header.Get("X-Nomad-LastContact") == "" {
   436  			t.Fatalf("missing last contact")
   437  		}
   438  
   439  		// Check the output
   440  		n := obj.(*structs.ACLToken)
   441  		assert.Equal(t, out, n)
   442  	})
   443  }
   444  
   445  func TestHTTP_ACLTokenCreate(t *testing.T) {
   446  	ci.Parallel(t)
   447  	httpACLTest(t, nil, func(s *TestAgent) {
   448  		// Make the HTTP request
   449  		p1 := mock.ACLToken()
   450  		p1.AccessorID = ""
   451  		buf := encodeReq(p1)
   452  		req, err := http.NewRequest("PUT", "/v1/acl/token", buf)
   453  		if err != nil {
   454  			t.Fatalf("err: %v", err)
   455  		}
   456  		respW := httptest.NewRecorder()
   457  		setToken(req, s.RootToken)
   458  
   459  		// Make the request
   460  		obj, err := s.Server.ACLTokenSpecificRequest(respW, req)
   461  		assert.Nil(t, err)
   462  		assert.NotNil(t, obj)
   463  		outTK := obj.(*structs.ACLToken)
   464  
   465  		// Check for the index
   466  		if respW.Result().Header.Get("X-Nomad-Index") == "" {
   467  			t.Fatalf("missing index")
   468  		}
   469  
   470  		// Check token was created
   471  		state := s.Agent.server.State()
   472  		out, err := state.ACLTokenByAccessorID(nil, outTK.AccessorID)
   473  		assert.Nil(t, err)
   474  		assert.NotNil(t, out)
   475  		assert.Equal(t, outTK, out)
   476  	})
   477  }
   478  
   479  func TestHTTP_ACLTokenCreateExpirationTTL(t *testing.T) {
   480  	ci.Parallel(t)
   481  	httpACLTest(t, nil, func(s *TestAgent) {
   482  
   483  		// Generate an example token which has an expiration TTL in string
   484  		// format.
   485  		aclToken := `
   486  {
   487    "Name": "Readonly token",
   488    "Type": "client",
   489    "Policies": ["readonly"],
   490    "ExpirationTTL": "10h",
   491    "Global": false
   492  }`
   493  
   494  		req, err := http.NewRequest("PUT", "/v1/acl/token", bytes.NewReader([]byte(aclToken)))
   495  		must.NoError(t, err)
   496  
   497  		respW := httptest.NewRecorder()
   498  		setToken(req, s.RootToken)
   499  
   500  		// Make the request.
   501  		obj, err := s.Server.ACLTokenSpecificRequest(respW, req)
   502  		must.NoError(t, err)
   503  		must.NotNil(t, obj)
   504  
   505  		// Ensure the returned token includes expiration.
   506  		createdTokenResp := obj.(*structs.ACLToken)
   507  		must.Eq(t, "10h0m0s", createdTokenResp.ExpirationTTL.String())
   508  		must.False(t, createdTokenResp.CreateTime.IsZero())
   509  
   510  		// Check for the index.
   511  		must.StrNotEqFold(t, "", respW.Result().Header.Get("X-Nomad-Index"))
   512  
   513  		// Check token was created and stored properly within state.
   514  		out, err := s.Agent.server.State().ACLTokenByAccessorID(nil, createdTokenResp.AccessorID)
   515  		must.NoError(t, err)
   516  		must.NotNil(t, out)
   517  		must.Eq(t, createdTokenResp, out)
   518  	})
   519  }
   520  
   521  func TestHTTP_ACLTokenDelete(t *testing.T) {
   522  	ci.Parallel(t)
   523  	httpACLTest(t, nil, func(s *TestAgent) {
   524  		p1 := mock.ACLToken()
   525  		p1.AccessorID = ""
   526  		args := structs.ACLTokenUpsertRequest{
   527  			Tokens: []*structs.ACLToken{p1},
   528  			WriteRequest: structs.WriteRequest{
   529  				Region:    "global",
   530  				AuthToken: s.RootToken.SecretID,
   531  			},
   532  		}
   533  		var resp structs.ACLTokenUpsertResponse
   534  		if err := s.Agent.RPC("ACL.UpsertTokens", &args, &resp); err != nil {
   535  			t.Fatalf("err: %v", err)
   536  		}
   537  		ID := resp.Tokens[0].AccessorID
   538  
   539  		// Make the HTTP request
   540  		req, err := http.NewRequest("DELETE", "/v1/acl/token/"+ID, nil)
   541  		if err != nil {
   542  			t.Fatalf("err: %v", err)
   543  		}
   544  		respW := httptest.NewRecorder()
   545  		setToken(req, s.RootToken)
   546  
   547  		// Make the request
   548  		obj, err := s.Server.ACLTokenSpecificRequest(respW, req)
   549  		assert.Nil(t, err)
   550  		assert.Nil(t, obj)
   551  
   552  		// Check for the index
   553  		if respW.Result().Header.Get("X-Nomad-Index") == "" {
   554  			t.Fatalf("missing index")
   555  		}
   556  
   557  		// Check token was created
   558  		state := s.Agent.server.State()
   559  		out, err := state.ACLTokenByAccessorID(nil, ID)
   560  		assert.Nil(t, err)
   561  		assert.Nil(t, out)
   562  	})
   563  }
   564  
   565  func TestHTTP_OneTimeToken(t *testing.T) {
   566  	ci.Parallel(t)
   567  	httpACLTest(t, nil, func(s *TestAgent) {
   568  
   569  		// Setup the ACL token
   570  
   571  		p1 := mock.ACLToken()
   572  		p1.AccessorID = ""
   573  		args := structs.ACLTokenUpsertRequest{
   574  			Tokens: []*structs.ACLToken{p1},
   575  			WriteRequest: structs.WriteRequest{
   576  				Region:    "global",
   577  				AuthToken: s.RootToken.SecretID,
   578  			},
   579  		}
   580  		var resp structs.ACLTokenUpsertResponse
   581  		err := s.Agent.RPC("ACL.UpsertTokens", &args, &resp)
   582  		require.NoError(t, err)
   583  		aclID := resp.Tokens[0].AccessorID
   584  		aclSecret := resp.Tokens[0].SecretID
   585  
   586  		// Make a HTTP request to get a one-time token
   587  
   588  		req, err := http.NewRequest("POST", "/v1/acl/token/onetime", nil)
   589  		require.NoError(t, err)
   590  		req.Header.Set("X-Nomad-Token", aclSecret)
   591  		respW := httptest.NewRecorder()
   592  
   593  		obj, err := s.Server.UpsertOneTimeToken(respW, req)
   594  		require.NoError(t, err)
   595  		require.NotNil(t, obj)
   596  
   597  		ott := obj.(structs.OneTimeTokenUpsertResponse)
   598  		require.Equal(t, aclID, ott.OneTimeToken.AccessorID)
   599  		require.NotEqual(t, "", ott.OneTimeToken.OneTimeSecretID)
   600  
   601  		// Make a HTTP request to exchange that token
   602  
   603  		buf := encodeReq(structs.OneTimeTokenExchangeRequest{
   604  			OneTimeSecretID: ott.OneTimeToken.OneTimeSecretID})
   605  		req, err = http.NewRequest("POST", "/v1/acl/token/onetime/exchange", buf)
   606  		require.NoError(t, err)
   607  		respW = httptest.NewRecorder()
   608  
   609  		obj, err = s.Server.ExchangeOneTimeToken(respW, req)
   610  		require.NoError(t, err)
   611  		require.NotNil(t, obj)
   612  
   613  		token := obj.(structs.OneTimeTokenExchangeResponse)
   614  		require.Equal(t, aclID, token.Token.AccessorID)
   615  		require.Equal(t, aclSecret, token.Token.SecretID)
   616  
   617  		// Making the same request a second time should return an error
   618  
   619  		buf = encodeReq(structs.OneTimeTokenExchangeRequest{
   620  			OneTimeSecretID: ott.OneTimeToken.OneTimeSecretID})
   621  		req, err = http.NewRequest("POST", "/v1/acl/token/onetime/exchange", buf)
   622  		require.NoError(t, err)
   623  		respW = httptest.NewRecorder()
   624  
   625  		obj, err = s.Server.ExchangeOneTimeToken(respW, req)
   626  		require.EqualError(t, err, structs.ErrPermissionDenied.Error())
   627  	})
   628  }
   629  
   630  func TestHTTPServer_ACLRoleListRequest(t *testing.T) {
   631  	ci.Parallel(t)
   632  
   633  	testCases := []struct {
   634  		name   string
   635  		testFn func(srv *TestAgent)
   636  	}{
   637  		{
   638  			name: "no auth token set",
   639  			testFn: func(srv *TestAgent) {
   640  
   641  				// Build the HTTP request.
   642  				req, err := http.NewRequest(http.MethodGet, "/v1/acl/roles", nil)
   643  				require.NoError(t, err)
   644  				respW := httptest.NewRecorder()
   645  
   646  				// Send the HTTP request.
   647  				obj, err := srv.Server.ACLRoleListRequest(respW, req)
   648  				require.NoError(t, err)
   649  				require.Empty(t, obj)
   650  			},
   651  		},
   652  		{
   653  			name: "invalid method",
   654  			testFn: func(srv *TestAgent) {
   655  
   656  				// Build the HTTP request.
   657  				req, err := http.NewRequest(http.MethodConnect, "/v1/acl/roles", nil)
   658  				require.NoError(t, err)
   659  				respW := httptest.NewRecorder()
   660  
   661  				// Ensure we have a token set.
   662  				setToken(req, srv.RootToken)
   663  
   664  				// Send the HTTP request.
   665  				obj, err := srv.Server.ACLRoleListRequest(respW, req)
   666  				require.Error(t, err)
   667  				require.ErrorContains(t, err, "Invalid method")
   668  				require.Nil(t, obj)
   669  			},
   670  		},
   671  		{
   672  			name: "no roles in state",
   673  			testFn: func(srv *TestAgent) {
   674  
   675  				// Build the HTTP request.
   676  				req, err := http.NewRequest(http.MethodGet, "/v1/acl/roles", nil)
   677  				require.NoError(t, err)
   678  				respW := httptest.NewRecorder()
   679  
   680  				// Ensure we have a token set.
   681  				setToken(req, srv.RootToken)
   682  
   683  				// Send the HTTP request.
   684  				obj, err := srv.Server.ACLRoleListRequest(respW, req)
   685  				require.NoError(t, err)
   686  				require.Empty(t, obj.([]*structs.ACLRoleListStub))
   687  			},
   688  		},
   689  		{
   690  			name: "roles in state",
   691  			testFn: func(srv *TestAgent) {
   692  
   693  				// Create the policies our ACL roles wants to link to.
   694  				policy1 := mock.ACLPolicy()
   695  				policy1.Name = "mocked-test-policy-1"
   696  				policy2 := mock.ACLPolicy()
   697  				policy2.Name = "mocked-test-policy-2"
   698  
   699  				require.NoError(t, srv.server.State().UpsertACLPolicies(
   700  					structs.MsgTypeTestSetup, 10, []*structs.ACLPolicy{policy1, policy2}))
   701  
   702  				// Create two ACL roles and put these directly into state.
   703  				aclRoles := []*structs.ACLRole{mock.ACLRole(), mock.ACLRole()}
   704  				require.NoError(t, srv.server.State().UpsertACLRoles(structs.MsgTypeTestSetup, 20, aclRoles, false))
   705  
   706  				// Build the HTTP request.
   707  				req, err := http.NewRequest(http.MethodGet, "/v1/acl/roles", nil)
   708  				require.NoError(t, err)
   709  				respW := httptest.NewRecorder()
   710  
   711  				// Ensure we have a token set.
   712  				setToken(req, srv.RootToken)
   713  
   714  				// Send the HTTP request.
   715  				obj, err := srv.Server.ACLRoleListRequest(respW, req)
   716  				require.NoError(t, err)
   717  				require.Len(t, obj.([]*structs.ACLRoleListStub), 2)
   718  			},
   719  		},
   720  		{
   721  			name: "roles in state using prefix",
   722  			testFn: func(srv *TestAgent) {
   723  
   724  				// Create the policies our ACL roles wants to link to.
   725  				policy1 := mock.ACLPolicy()
   726  				policy1.Name = "mocked-test-policy-1"
   727  				policy2 := mock.ACLPolicy()
   728  				policy2.Name = "mocked-test-policy-2"
   729  
   730  				require.NoError(t, srv.server.State().UpsertACLPolicies(
   731  					structs.MsgTypeTestSetup, 10, []*structs.ACLPolicy{policy1, policy2}))
   732  
   733  				// Create two ACL roles and put these directly into state, one
   734  				// using a custom prefix.
   735  				aclRoles := []*structs.ACLRole{mock.ACLRole(), mock.ACLRole()}
   736  				aclRoles[1].ID = "badger-badger-badger-" + uuid.Generate()
   737  				require.NoError(t, srv.server.State().UpsertACLRoles(structs.MsgTypeTestSetup, 20, aclRoles, false))
   738  
   739  				// Build the HTTP request.
   740  				req, err := http.NewRequest(http.MethodGet, "/v1/acl/roles?prefix=badger-badger-badger", nil)
   741  				require.NoError(t, err)
   742  				respW := httptest.NewRecorder()
   743  
   744  				// Ensure we have a token set.
   745  				setToken(req, srv.RootToken)
   746  
   747  				// Send the HTTP request.
   748  				obj, err := srv.Server.ACLRoleListRequest(respW, req)
   749  				require.NoError(t, err)
   750  				require.Len(t, obj.([]*structs.ACLRoleListStub), 1)
   751  				require.Contains(t, obj.([]*structs.ACLRoleListStub)[0].ID, "badger-badger-badger")
   752  			},
   753  		},
   754  	}
   755  
   756  	for _, tc := range testCases {
   757  		t.Run(tc.name, func(t *testing.T) {
   758  			httpACLTest(t, nil, tc.testFn)
   759  		})
   760  	}
   761  }
   762  
   763  func TestHTTPServer_ACLRoleRequest(t *testing.T) {
   764  	ci.Parallel(t)
   765  
   766  	testCases := []struct {
   767  		name   string
   768  		testFn func(srv *TestAgent)
   769  	}{
   770  		{
   771  			name: "no auth token set",
   772  			testFn: func(srv *TestAgent) {
   773  
   774  				// Create a mock role to use in the request body.
   775  				mockACLRole := mock.ACLRole()
   776  				mockACLRole.ID = ""
   777  
   778  				// Build the HTTP request.
   779  				req, err := http.NewRequest(http.MethodPut, "/v1/acl/role", encodeReq(mockACLRole))
   780  				require.NoError(t, err)
   781  				respW := httptest.NewRecorder()
   782  
   783  				// Send the HTTP request.
   784  				obj, err := srv.Server.ACLRoleRequest(respW, req)
   785  				require.Error(t, err)
   786  				require.ErrorContains(t, err, "Permission denied")
   787  				require.Nil(t, obj)
   788  			},
   789  		},
   790  		{
   791  			name: "invalid method",
   792  			testFn: func(srv *TestAgent) {
   793  
   794  				// Build the HTTP request.
   795  				req, err := http.NewRequest(http.MethodConnect, "/v1/acl/role", nil)
   796  				require.NoError(t, err)
   797  				respW := httptest.NewRecorder()
   798  
   799  				// Ensure we have a token set.
   800  				setToken(req, srv.RootToken)
   801  
   802  				// Send the HTTP request.
   803  				obj, err := srv.Server.ACLRoleRequest(respW, req)
   804  				require.Error(t, err)
   805  				require.ErrorContains(t, err, "Invalid method")
   806  				require.Nil(t, obj)
   807  			},
   808  		},
   809  		{
   810  			name: "successful upsert",
   811  			testFn: func(srv *TestAgent) {
   812  
   813  				// Create the policies our ACL roles wants to link to.
   814  				policy1 := mock.ACLPolicy()
   815  				policy1.Name = "mocked-test-policy-1"
   816  				policy2 := mock.ACLPolicy()
   817  				policy2.Name = "mocked-test-policy-2"
   818  
   819  				require.NoError(t, srv.server.State().UpsertACLPolicies(
   820  					structs.MsgTypeTestSetup, 10, []*structs.ACLPolicy{policy1, policy2}))
   821  
   822  				// Create a mock role to use in the request body.
   823  				mockACLRole := mock.ACLRole()
   824  				mockACLRole.ID = ""
   825  
   826  				// Build the HTTP request.
   827  				req, err := http.NewRequest(http.MethodPut, "/v1/acl/role", encodeReq(mockACLRole))
   828  				require.NoError(t, err)
   829  				respW := httptest.NewRecorder()
   830  
   831  				// Ensure we have a token set.
   832  				setToken(req, srv.RootToken)
   833  
   834  				// Send the HTTP request.
   835  				obj, err := srv.Server.ACLRoleRequest(respW, req)
   836  				require.NoError(t, err)
   837  				require.NotNil(t, obj)
   838  				require.Equal(t, obj.(*structs.ACLRole).Hash, mockACLRole.Hash)
   839  			},
   840  		},
   841  	}
   842  
   843  	for _, tc := range testCases {
   844  		t.Run(tc.name, func(t *testing.T) {
   845  			httpACLTest(t, nil, tc.testFn)
   846  		})
   847  	}
   848  }
   849  
   850  func TestHTTPServer_ACLRoleSpecificRequest(t *testing.T) {
   851  	ci.Parallel(t)
   852  
   853  	testCases := []struct {
   854  		name   string
   855  		testFn func(srv *TestAgent)
   856  	}{
   857  		{
   858  			name: "invalid URI",
   859  			testFn: func(srv *TestAgent) {
   860  
   861  				// Build the HTTP request.
   862  				req, err := http.NewRequest(http.MethodGet, "/v1/acl/role/name/this/is/will/not/work", nil)
   863  				require.NoError(t, err)
   864  				respW := httptest.NewRecorder()
   865  
   866  				// Send the HTTP request.
   867  				obj, err := srv.Server.ACLRoleSpecificRequest(respW, req)
   868  				require.Error(t, err)
   869  				require.ErrorContains(t, err, "invalid URI")
   870  				require.Nil(t, obj)
   871  			},
   872  		},
   873  		{
   874  			name: "invalid role name lookalike URI",
   875  			testFn: func(srv *TestAgent) {
   876  
   877  				// Build the HTTP request.
   878  				req, err := http.NewRequest(http.MethodGet, "/v1/acl/role/foobar/rolename", nil)
   879  				require.NoError(t, err)
   880  				respW := httptest.NewRecorder()
   881  
   882  				// Send the HTTP request.
   883  				obj, err := srv.Server.ACLRoleSpecificRequest(respW, req)
   884  				require.Error(t, err)
   885  				require.ErrorContains(t, err, "invalid URI")
   886  				require.Nil(t, obj)
   887  			},
   888  		},
   889  		{
   890  			name: "missing role name",
   891  			testFn: func(srv *TestAgent) {
   892  
   893  				// Build the HTTP request.
   894  				req, err := http.NewRequest(http.MethodGet, "/v1/acl/role/name/", nil)
   895  				require.NoError(t, err)
   896  				respW := httptest.NewRecorder()
   897  
   898  				// Send the HTTP request.
   899  				obj, err := srv.Server.ACLRoleSpecificRequest(respW, req)
   900  				require.Error(t, err)
   901  				require.ErrorContains(t, err, "missing ACL role name")
   902  				require.Nil(t, obj)
   903  			},
   904  		},
   905  		{
   906  			name: "missing role ID",
   907  			testFn: func(srv *TestAgent) {
   908  
   909  				// Build the HTTP request.
   910  				req, err := http.NewRequest(http.MethodGet, "/v1/acl/role/", nil)
   911  				require.NoError(t, err)
   912  				respW := httptest.NewRecorder()
   913  
   914  				// Send the HTTP request.
   915  				obj, err := srv.Server.ACLRoleSpecificRequest(respW, req)
   916  				require.Error(t, err)
   917  				require.ErrorContains(t, err, "missing ACL role ID")
   918  				require.Nil(t, obj)
   919  			},
   920  		},
   921  		{
   922  			name: "role name incorrect method",
   923  			testFn: func(srv *TestAgent) {
   924  
   925  				// Build the HTTP request.
   926  				req, err := http.NewRequest(http.MethodConnect, "/v1/acl/role/name/foobar", nil)
   927  				require.NoError(t, err)
   928  				respW := httptest.NewRecorder()
   929  
   930  				// Send the HTTP request.
   931  				obj, err := srv.Server.ACLRoleSpecificRequest(respW, req)
   932  				require.Error(t, err)
   933  				require.ErrorContains(t, err, "Invalid method")
   934  				require.Nil(t, obj)
   935  			},
   936  		},
   937  		{
   938  			name: "role ID incorrect method",
   939  			testFn: func(srv *TestAgent) {
   940  
   941  				// Build the HTTP request.
   942  				req, err := http.NewRequest(http.MethodConnect, "/v1/acl/role/foobar", nil)
   943  				require.NoError(t, err)
   944  				respW := httptest.NewRecorder()
   945  
   946  				// Send the HTTP request.
   947  				obj, err := srv.Server.ACLRoleSpecificRequest(respW, req)
   948  				require.Error(t, err)
   949  				require.ErrorContains(t, err, "Invalid method")
   950  				require.Nil(t, obj)
   951  			},
   952  		},
   953  		{
   954  			name: "get role by name",
   955  			testFn: func(srv *TestAgent) {
   956  
   957  				// Create the policies our ACL roles wants to link to.
   958  				policy1 := mock.ACLPolicy()
   959  				policy1.Name = "mocked-test-policy-1"
   960  				policy2 := mock.ACLPolicy()
   961  				policy2.Name = "mocked-test-policy-2"
   962  
   963  				require.NoError(t, srv.server.State().UpsertACLPolicies(
   964  					structs.MsgTypeTestSetup, 10, []*structs.ACLPolicy{policy1, policy2}))
   965  
   966  				// Create a mock role and put directly into state.
   967  				mockACLRole := mock.ACLRole()
   968  				require.NoError(t, srv.server.State().UpsertACLRoles(
   969  					structs.MsgTypeTestSetup, 20, []*structs.ACLRole{mockACLRole}, false))
   970  
   971  				url := fmt.Sprintf("/v1/acl/role/name/%s", mockACLRole.Name)
   972  
   973  				// Build the HTTP request.
   974  				req, err := http.NewRequest(http.MethodGet, url, nil)
   975  				require.NoError(t, err)
   976  				respW := httptest.NewRecorder()
   977  
   978  				// Ensure we have a token set.
   979  				setToken(req, srv.RootToken)
   980  
   981  				// Send the HTTP request.
   982  				obj, err := srv.Server.ACLRoleSpecificRequest(respW, req)
   983  				require.NoError(t, err)
   984  				require.Equal(t, obj.(*structs.ACLRole).Hash, mockACLRole.Hash)
   985  			},
   986  		},
   987  		{
   988  			name: "get, update, and delete role by ID",
   989  			testFn: func(srv *TestAgent) {
   990  
   991  				// Create the policies our ACL roles wants to link to.
   992  				policy1 := mock.ACLPolicy()
   993  				policy1.Name = "mocked-test-policy-1"
   994  				policy2 := mock.ACLPolicy()
   995  				policy2.Name = "mocked-test-policy-2"
   996  
   997  				require.NoError(t, srv.server.State().UpsertACLPolicies(
   998  					structs.MsgTypeTestSetup, 10, []*structs.ACLPolicy{policy1, policy2}))
   999  
  1000  				// Create a mock role and put directly into state.
  1001  				mockACLRole := mock.ACLRole()
  1002  				require.NoError(t, srv.server.State().UpsertACLRoles(
  1003  					structs.MsgTypeTestSetup, 20, []*structs.ACLRole{mockACLRole}, false))
  1004  
  1005  				url := fmt.Sprintf("/v1/acl/role/%s", mockACLRole.ID)
  1006  
  1007  				// Build the HTTP request to read the role using its ID.
  1008  				req, err := http.NewRequest(http.MethodGet, url, nil)
  1009  				require.NoError(t, err)
  1010  				respW := httptest.NewRecorder()
  1011  
  1012  				// Ensure we have a token set.
  1013  				setToken(req, srv.RootToken)
  1014  
  1015  				// Send the HTTP request.
  1016  				obj, err := srv.Server.ACLRoleSpecificRequest(respW, req)
  1017  				require.NoError(t, err)
  1018  				require.Equal(t, obj.(*structs.ACLRole).Hash, mockACLRole.Hash)
  1019  
  1020  				// Update the role policy list and make the request via the
  1021  				// HTTP API.
  1022  				mockACLRole.Policies = []*structs.ACLRolePolicyLink{{Name: "mocked-test-policy-1"}}
  1023  
  1024  				req, err = http.NewRequest(http.MethodPost, url, encodeReq(mockACLRole))
  1025  				require.NoError(t, err)
  1026  				respW = httptest.NewRecorder()
  1027  
  1028  				// Ensure we have a token set.
  1029  				setToken(req, srv.RootToken)
  1030  
  1031  				// Send the HTTP request.
  1032  				obj, err = srv.Server.ACLRoleSpecificRequest(respW, req)
  1033  				require.NoError(t, err)
  1034  				require.Equal(t, obj.(*structs.ACLRole).Policies, mockACLRole.Policies)
  1035  
  1036  				// Delete the ACL role using its ID.
  1037  				req, err = http.NewRequest(http.MethodDelete, url, nil)
  1038  				require.NoError(t, err)
  1039  				respW = httptest.NewRecorder()
  1040  
  1041  				// Ensure we have a token set.
  1042  				setToken(req, srv.RootToken)
  1043  
  1044  				// Send the HTTP request.
  1045  				obj, err = srv.Server.ACLRoleSpecificRequest(respW, req)
  1046  				require.NoError(t, err)
  1047  				require.Nil(t, obj)
  1048  
  1049  				// Ensure the ACL role is no longer stored within state.
  1050  				aclRole, err := srv.server.State().GetACLRoleByID(nil, mockACLRole.ID)
  1051  				require.NoError(t, err)
  1052  				require.Nil(t, aclRole)
  1053  			},
  1054  		},
  1055  	}
  1056  
  1057  	for _, tc := range testCases {
  1058  		t.Run(tc.name, func(t *testing.T) {
  1059  			httpACLTest(t, nil, tc.testFn)
  1060  		})
  1061  	}
  1062  }
  1063  
  1064  func TestHTTPServer_ACLAuthMethodListRequest(t *testing.T) {
  1065  	ci.Parallel(t)
  1066  
  1067  	testCases := []struct {
  1068  		name   string
  1069  		testFn func(srv *TestAgent)
  1070  	}{
  1071  		{
  1072  			name: "no auth token set",
  1073  			testFn: func(srv *TestAgent) {
  1074  
  1075  				// Build the HTTP request.
  1076  				req, err := http.NewRequest(http.MethodGet, "/v1/acl/auth-methods", nil)
  1077  				must.NoError(t, err)
  1078  				respW := httptest.NewRecorder()
  1079  
  1080  				// Send the HTTP request.
  1081  				obj, err := srv.Server.ACLAuthMethodListRequest(respW, req)
  1082  				must.NoError(t, err)
  1083  				must.Len(t, 0, obj.([]*structs.ACLAuthMethodStub))
  1084  			},
  1085  		},
  1086  		{
  1087  			name: "invalid method",
  1088  			testFn: func(srv *TestAgent) {
  1089  
  1090  				// Build the HTTP request.
  1091  				req, err := http.NewRequest(http.MethodConnect, "/v1/acl/auth-methods", nil)
  1092  				must.NoError(t, err)
  1093  				respW := httptest.NewRecorder()
  1094  
  1095  				// Ensure we have a token set.
  1096  				setToken(req, srv.RootToken)
  1097  
  1098  				// Send the HTTP request.
  1099  				obj, err := srv.Server.ACLAuthMethodListRequest(respW, req)
  1100  				must.Error(t, err)
  1101  				must.StrContains(t, err.Error(), "Invalid method")
  1102  				must.Nil(t, obj)
  1103  			},
  1104  		},
  1105  		{
  1106  			name: "no auth-methods in state",
  1107  			testFn: func(srv *TestAgent) {
  1108  
  1109  				// Build the HTTP request.
  1110  				req, err := http.NewRequest(http.MethodGet, "/v1/acl/auth-methods", nil)
  1111  				must.NoError(t, err)
  1112  				respW := httptest.NewRecorder()
  1113  
  1114  				// Ensure we have a token set.
  1115  				setToken(req, srv.RootToken)
  1116  
  1117  				// Send the HTTP request.
  1118  				obj, err := srv.Server.ACLAuthMethodListRequest(respW, req)
  1119  				must.NoError(t, err)
  1120  				must.Len(t, 0, obj.([]*structs.ACLAuthMethodStub))
  1121  			},
  1122  		},
  1123  		{
  1124  			name: "auth-methods in state",
  1125  			testFn: func(srv *TestAgent) {
  1126  
  1127  				// Upsert two auth-methods into state.
  1128  				must.NoError(t, srv.server.State().UpsertACLAuthMethods(
  1129  					10, []*structs.ACLAuthMethod{mock.ACLOIDCAuthMethod(), mock.ACLOIDCAuthMethod()}))
  1130  
  1131  				// Build the HTTP request.
  1132  				req, err := http.NewRequest(http.MethodGet, "/v1/acl/auth-methods", nil)
  1133  				must.NoError(t, err)
  1134  				respW := httptest.NewRecorder()
  1135  
  1136  				// Ensure we have a token set.
  1137  				setToken(req, srv.RootToken)
  1138  
  1139  				// Send the HTTP request.
  1140  				obj, err := srv.Server.ACLAuthMethodListRequest(respW, req)
  1141  				must.NoError(t, err)
  1142  				must.Len(t, 2, obj.([]*structs.ACLAuthMethodStub))
  1143  			},
  1144  		},
  1145  	}
  1146  
  1147  	for _, tc := range testCases {
  1148  		t.Run(tc.name, func(t *testing.T) {
  1149  			httpACLTest(t, nil, tc.testFn)
  1150  		})
  1151  	}
  1152  }
  1153  
  1154  func TestHTTPServer_ACLAuthMethodRequest(t *testing.T) {
  1155  	ci.Parallel(t)
  1156  
  1157  	testCases := []struct {
  1158  		name   string
  1159  		testFn func(srv *TestAgent)
  1160  	}{
  1161  		{
  1162  			name: "no auth token set",
  1163  			testFn: func(srv *TestAgent) {
  1164  
  1165  				// Create a mock role to use in the request body.
  1166  				mockACLRole := mock.ACLRole()
  1167  				mockACLRole.ID = ""
  1168  
  1169  				// Build the HTTP request.
  1170  				req, err := http.NewRequest(http.MethodPut, "/v1/acl/auth-method", encodeReq(mockACLRole))
  1171  				must.NoError(t, err)
  1172  				respW := httptest.NewRecorder()
  1173  
  1174  				// Send the HTTP request.
  1175  				obj, err := srv.Server.ACLAuthMethodRequest(respW, req)
  1176  				must.Error(t, err)
  1177  				must.StrContains(t, err.Error(), "Permission denied")
  1178  				must.Nil(t, obj)
  1179  			},
  1180  		},
  1181  		{
  1182  			name: "invalid method",
  1183  			testFn: func(srv *TestAgent) {
  1184  
  1185  				// Build the HTTP request.
  1186  				req, err := http.NewRequest(http.MethodConnect, "/v1/acl/auth-method", nil)
  1187  				must.NoError(t, err)
  1188  				respW := httptest.NewRecorder()
  1189  
  1190  				// Ensure we have a token set.
  1191  				setToken(req, srv.RootToken)
  1192  
  1193  				// Send the HTTP request.
  1194  				obj, err := srv.Server.ACLAuthMethodRequest(respW, req)
  1195  				must.Error(t, err)
  1196  				must.StrContains(t, err.Error(), "Invalid method")
  1197  				must.Nil(t, obj)
  1198  			},
  1199  		},
  1200  		{
  1201  			name: "successful upsert",
  1202  			testFn: func(srv *TestAgent) {
  1203  
  1204  				// Create a mock auth-method to use in the request body.
  1205  				mockACLAuthMethod := mock.ACLOIDCAuthMethod()
  1206  
  1207  				// Build the HTTP request.
  1208  				req, err := http.NewRequest(http.MethodPut, "/v1/acl/auth-method", encodeReq(mockACLAuthMethod))
  1209  				must.NoError(t, err)
  1210  				respW := httptest.NewRecorder()
  1211  
  1212  				// Ensure we have a token set.
  1213  				setToken(req, srv.RootToken)
  1214  
  1215  				// Send the HTTP request.
  1216  				obj, err := srv.Server.ACLAuthMethodRequest(respW, req)
  1217  				must.NoError(t, err)
  1218  
  1219  				result := obj.(*structs.ACLAuthMethod)
  1220  				must.Eq(t, result, mockACLAuthMethod)
  1221  			},
  1222  		},
  1223  	}
  1224  
  1225  	for _, tc := range testCases {
  1226  		t.Run(tc.name, func(t *testing.T) {
  1227  			httpACLTest(t, nil, tc.testFn)
  1228  		})
  1229  	}
  1230  }
  1231  
  1232  func TestHTTPServer_ACLAuthMethodSpecificRequest(t *testing.T) {
  1233  	ci.Parallel(t)
  1234  
  1235  	testCases := []struct {
  1236  		name   string
  1237  		testFn func(srv *TestAgent)
  1238  	}{
  1239  		{
  1240  			name: "missing auth-method name",
  1241  			testFn: func(srv *TestAgent) {
  1242  
  1243  				// Build the HTTP request.
  1244  				req, err := http.NewRequest(http.MethodGet, "/v1/acl/auth-method/", nil)
  1245  				must.NoError(t, err)
  1246  				respW := httptest.NewRecorder()
  1247  
  1248  				// Send the HTTP request.
  1249  				obj, err := srv.Server.ACLAuthMethodSpecificRequest(respW, req)
  1250  				must.Error(t, err)
  1251  				must.StrContains(t, err.Error(), "missing ACL auth-method name")
  1252  				must.Nil(t, obj)
  1253  			},
  1254  		},
  1255  		{
  1256  			name: "incorrect method",
  1257  			testFn: func(srv *TestAgent) {
  1258  
  1259  				// Build the HTTP request.
  1260  				req, err := http.NewRequest(http.MethodConnect, "/v1/acl/auth-method/foobar", nil)
  1261  				must.NoError(t, err)
  1262  				respW := httptest.NewRecorder()
  1263  
  1264  				// Send the HTTP request.
  1265  				obj, err := srv.Server.ACLAuthMethodSpecificRequest(respW, req)
  1266  				must.Error(t, err)
  1267  				must.StrContains(t, err.Error(), "Invalid method")
  1268  				must.Nil(t, obj)
  1269  			},
  1270  		},
  1271  		{
  1272  			name: "get auth-method",
  1273  			testFn: func(srv *TestAgent) {
  1274  
  1275  				// Create a mock auth-method and put directly into state.
  1276  				mockACLAuthMethod := mock.ACLOIDCAuthMethod()
  1277  				must.NoError(t, srv.server.State().UpsertACLAuthMethods(
  1278  					20, []*structs.ACLAuthMethod{mockACLAuthMethod}))
  1279  
  1280  				authMethodURL := "/v1/acl/auth-method/" + mockACLAuthMethod.Name
  1281  
  1282  				// Build the HTTP request.
  1283  				req, err := http.NewRequest(http.MethodGet, authMethodURL, nil)
  1284  				must.NoError(t, err)
  1285  				respW := httptest.NewRecorder()
  1286  
  1287  				// Ensure we have a token set.
  1288  				setToken(req, srv.RootToken)
  1289  
  1290  				// Send the HTTP request.
  1291  				obj, err := srv.Server.ACLAuthMethodSpecificRequest(respW, req)
  1292  				must.NoError(t, err)
  1293  				must.Eq(t, obj.(*structs.ACLAuthMethod).Hash, mockACLAuthMethod.Hash)
  1294  			},
  1295  		},
  1296  		{
  1297  			name: "get, update, and delete auth-method",
  1298  			testFn: func(srv *TestAgent) {
  1299  
  1300  				// Create a mock auth-method and put directly into state.
  1301  				mockACLAuthMethod := mock.ACLOIDCAuthMethod()
  1302  				must.NoError(t, srv.server.State().UpsertACLAuthMethods(
  1303  					20, []*structs.ACLAuthMethod{mockACLAuthMethod}))
  1304  
  1305  				authMethodURL := "/v1/acl/auth-method/" + mockACLAuthMethod.Name
  1306  
  1307  				// Build the HTTP request to read the auth-method.
  1308  				req, err := http.NewRequest(http.MethodGet, authMethodURL, nil)
  1309  				must.NoError(t, err)
  1310  				respW := httptest.NewRecorder()
  1311  
  1312  				// Ensure we have a token set.
  1313  				setToken(req, srv.RootToken)
  1314  
  1315  				// Send the HTTP request.
  1316  				obj, err := srv.Server.ACLAuthMethodSpecificRequest(respW, req)
  1317  				must.NoError(t, err)
  1318  				must.Eq(t, obj.(*structs.ACLAuthMethod).Hash, mockACLAuthMethod.Hash)
  1319  
  1320  				// Update the auth-method and make the request via the HTTP
  1321  				// API.
  1322  				mockACLAuthMethod.MaxTokenTTL = 3600 * time.Hour
  1323  				mockACLAuthMethod.SetHash()
  1324  
  1325  				req, err = http.NewRequest(http.MethodPost, authMethodURL, encodeReq(mockACLAuthMethod))
  1326  				must.NoError(t, err)
  1327  				respW = httptest.NewRecorder()
  1328  
  1329  				// Ensure we have a token set.
  1330  				setToken(req, srv.RootToken)
  1331  
  1332  				// Send the HTTP request.
  1333  				_, err = srv.Server.ACLAuthMethodSpecificRequest(respW, req)
  1334  				must.NoError(t, err)
  1335  
  1336  				// Delete the ACL auth-method.
  1337  				req, err = http.NewRequest(http.MethodDelete, authMethodURL, nil)
  1338  				must.NoError(t, err)
  1339  				respW = httptest.NewRecorder()
  1340  
  1341  				// Ensure we have a token set.
  1342  				setToken(req, srv.RootToken)
  1343  
  1344  				// Send the HTTP request.
  1345  				obj, err = srv.Server.ACLAuthMethodSpecificRequest(respW, req)
  1346  				must.NoError(t, err)
  1347  				must.Nil(t, obj)
  1348  
  1349  				// Ensure the ACL auth-method is no longer stored within state.
  1350  				aclAuthMethod, err := srv.server.State().GetACLAuthMethodByName(nil, mockACLAuthMethod.Name)
  1351  				must.NoError(t, err)
  1352  				must.Nil(t, aclAuthMethod)
  1353  			},
  1354  		},
  1355  	}
  1356  
  1357  	for _, tc := range testCases {
  1358  		t.Run(tc.name, func(t *testing.T) {
  1359  			cb := func(c *Config) { c.NomadConfig.ACLTokenMaxExpirationTTL = 3600 * time.Hour }
  1360  			httpACLTest(t, cb, tc.testFn)
  1361  		})
  1362  	}
  1363  }
  1364  
  1365  func TestHTTPServer_ACLBindingRuleListRequest(t *testing.T) {
  1366  	ci.Parallel(t)
  1367  
  1368  	testCases := []struct {
  1369  		name   string
  1370  		testFn func(srv *TestAgent)
  1371  	}{
  1372  		{
  1373  			name: "no auth token set",
  1374  			testFn: func(srv *TestAgent) {
  1375  
  1376  				// Build the HTTP request.
  1377  				req, err := http.NewRequest(http.MethodGet, "/v1/acl/binding-rules", nil)
  1378  				must.NoError(t, err)
  1379  				respW := httptest.NewRecorder()
  1380  
  1381  				// Send the HTTP request.
  1382  				obj, err := srv.Server.ACLBindingRuleListRequest(respW, req)
  1383  				must.EqError(t, err, "Permission denied")
  1384  				must.Nil(t, obj)
  1385  			},
  1386  		},
  1387  		{
  1388  			name: "invalid method",
  1389  			testFn: func(srv *TestAgent) {
  1390  
  1391  				// Build the HTTP request.
  1392  				req, err := http.NewRequest(http.MethodConnect, "/v1/acl/binding-rules", nil)
  1393  				must.NoError(t, err)
  1394  				respW := httptest.NewRecorder()
  1395  
  1396  				// Ensure we have a token set.
  1397  				setToken(req, srv.RootToken)
  1398  
  1399  				// Send the HTTP request.
  1400  				obj, err := srv.Server.ACLBindingRuleListRequest(respW, req)
  1401  				must.EqError(t, err, "Invalid method")
  1402  				must.Nil(t, obj)
  1403  			},
  1404  		},
  1405  		{
  1406  			name: "no binding rules in state",
  1407  			testFn: func(srv *TestAgent) {
  1408  
  1409  				// Build the HTTP request.
  1410  				req, err := http.NewRequest(http.MethodGet, "/v1/acl/binding-rules", nil)
  1411  				must.NoError(t, err)
  1412  				respW := httptest.NewRecorder()
  1413  
  1414  				// Ensure we have a token set.
  1415  				setToken(req, srv.RootToken)
  1416  
  1417  				// Send the HTTP request.
  1418  				obj, err := srv.Server.ACLBindingRuleListRequest(respW, req)
  1419  				must.NoError(t, err)
  1420  				must.Len(t, 0, obj.([]*structs.ACLBindingRuleListStub))
  1421  			},
  1422  		},
  1423  		{
  1424  			name: "binding rules in state",
  1425  			testFn: func(srv *TestAgent) {
  1426  
  1427  				// Upsert two binding rules into state.
  1428  				must.NoError(t, srv.server.State().UpsertACLBindingRules(
  1429  					10, []*structs.ACLBindingRule{mock.ACLBindingRule(), mock.ACLBindingRule()}, true))
  1430  
  1431  				// Build the HTTP request.
  1432  				req, err := http.NewRequest(http.MethodGet, "/v1/acl/binding-rules", nil)
  1433  				must.NoError(t, err)
  1434  				respW := httptest.NewRecorder()
  1435  
  1436  				// Ensure we have a token set.
  1437  				setToken(req, srv.RootToken)
  1438  
  1439  				// Send the HTTP request.
  1440  				obj, err := srv.Server.ACLBindingRuleListRequest(respW, req)
  1441  				must.NoError(t, err)
  1442  				must.Len(t, 2, obj.([]*structs.ACLBindingRuleListStub))
  1443  			},
  1444  		},
  1445  	}
  1446  
  1447  	for _, tc := range testCases {
  1448  		t.Run(tc.name, func(t *testing.T) {
  1449  			httpACLTest(t, nil, tc.testFn)
  1450  		})
  1451  	}
  1452  }
  1453  
  1454  func TestHTTPServer_ACLBindingRuleRequest(t *testing.T) {
  1455  	ci.Parallel(t)
  1456  
  1457  	testCases := []struct {
  1458  		name   string
  1459  		testFn func(srv *TestAgent)
  1460  	}{
  1461  		{
  1462  			name: "no auth token set",
  1463  			testFn: func(srv *TestAgent) {
  1464  
  1465  				// Create a mock binding rule to use in the request body.
  1466  				mockACLBindingRule := mock.ACLBindingRule()
  1467  				mockACLBindingRule.ID = ""
  1468  
  1469  				// Build the HTTP request.
  1470  				req, err := http.NewRequest(http.MethodPut, "/v1/acl/binding-rule", encodeReq(mockACLBindingRule))
  1471  				must.NoError(t, err)
  1472  				respW := httptest.NewRecorder()
  1473  
  1474  				// Send the HTTP request.
  1475  				obj, err := srv.Server.ACLBindingRuleRequest(respW, req)
  1476  				must.Error(t, err)
  1477  				must.StrContains(t, err.Error(), "Permission denied")
  1478  				must.Nil(t, obj)
  1479  			},
  1480  		},
  1481  		{
  1482  			name: "invalid method",
  1483  			testFn: func(srv *TestAgent) {
  1484  
  1485  				// Build the HTTP request.
  1486  				req, err := http.NewRequest(http.MethodConnect, "/v1/acl/binding-rule", nil)
  1487  				must.NoError(t, err)
  1488  				respW := httptest.NewRecorder()
  1489  
  1490  				// Ensure we have a token set.
  1491  				setToken(req, srv.RootToken)
  1492  
  1493  				// Send the HTTP request.
  1494  				obj, err := srv.Server.ACLBindingRuleRequest(respW, req)
  1495  				must.Error(t, err)
  1496  				must.StrContains(t, err.Error(), "Invalid method")
  1497  				must.Nil(t, obj)
  1498  			},
  1499  		},
  1500  		{
  1501  			name: "successful upsert",
  1502  			testFn: func(srv *TestAgent) {
  1503  
  1504  				// Upsert the auth method that the binding rule will associate
  1505  				// with.
  1506  				mockACLAuthMethod := mock.ACLOIDCAuthMethod()
  1507  				must.NoError(t, srv.server.State().UpsertACLAuthMethods(
  1508  					10, []*structs.ACLAuthMethod{mockACLAuthMethod}))
  1509  
  1510  				// Create a mock binding rule to use in the request body.
  1511  				mockACLBindingRule := mock.ACLBindingRule()
  1512  				mockACLBindingRule.AuthMethod = mockACLAuthMethod.Name
  1513  				mockACLBindingRule.ID = ""
  1514  
  1515  				// Build the HTTP request.
  1516  				req, err := http.NewRequest(http.MethodPut, "/v1/acl/binding-rule", encodeReq(mockACLBindingRule))
  1517  				must.NoError(t, err)
  1518  				respW := httptest.NewRecorder()
  1519  
  1520  				// Ensure we have a token set.
  1521  				setToken(req, srv.RootToken)
  1522  
  1523  				// Send the HTTP request.
  1524  				obj, err := srv.Server.ACLBindingRuleRequest(respW, req)
  1525  				must.NoError(t, err)
  1526  
  1527  				result := obj.(*structs.ACLBindingRule)
  1528  				must.Eq(t, mockACLBindingRule.Selector, result.Selector)
  1529  				must.Eq(t, mockACLBindingRule.AuthMethod, result.AuthMethod)
  1530  				must.Eq(t, mockACLBindingRule.Description, result.Description)
  1531  			},
  1532  		},
  1533  	}
  1534  
  1535  	for _, tc := range testCases {
  1536  		t.Run(tc.name, func(t *testing.T) {
  1537  			httpACLTest(t, nil, tc.testFn)
  1538  		})
  1539  	}
  1540  }
  1541  
  1542  func TestHTTPServer_ACLBindingRuleSpecificRequest(t *testing.T) {
  1543  	ci.Parallel(t)
  1544  
  1545  	testCases := []struct {
  1546  		name   string
  1547  		testFn func(srv *TestAgent)
  1548  	}{
  1549  		{
  1550  			name: "missing binding rule ID",
  1551  			testFn: func(srv *TestAgent) {
  1552  
  1553  				// Build the HTTP request.
  1554  				req, err := http.NewRequest(http.MethodGet, "/v1/acl/binding-rule/", nil)
  1555  				must.NoError(t, err)
  1556  				respW := httptest.NewRecorder()
  1557  
  1558  				// Send the HTTP request.
  1559  				obj, err := srv.Server.ACLBindingRuleSpecificRequest(respW, req)
  1560  				must.EqError(t, err, "missing ACL binding rule ID")
  1561  				must.Nil(t, obj)
  1562  			},
  1563  		},
  1564  		{
  1565  			name: "incorrect method",
  1566  			testFn: func(srv *TestAgent) {
  1567  
  1568  				// Build the HTTP request.
  1569  				req, err := http.NewRequest(http.MethodConnect, "/v1/acl/binding-rule/foobar", nil)
  1570  				must.NoError(t, err)
  1571  				respW := httptest.NewRecorder()
  1572  
  1573  				// Send the HTTP request.
  1574  				obj, err := srv.Server.ACLBindingRuleSpecificRequest(respW, req)
  1575  				must.EqError(t, err, "Invalid method")
  1576  				must.Nil(t, obj)
  1577  			},
  1578  		},
  1579  		{
  1580  			name: "get binding rule",
  1581  			testFn: func(srv *TestAgent) {
  1582  
  1583  				// Upsert a binding rule into state.
  1584  				aclBindingRules := []*structs.ACLBindingRule{mock.ACLBindingRule()}
  1585  				must.NoError(t, srv.server.State().UpsertACLBindingRules(10, aclBindingRules, true))
  1586  
  1587  				url := "/v1/acl/binding-rule/" + aclBindingRules[0].ID
  1588  
  1589  				// Build the HTTP request.
  1590  				req, err := http.NewRequest(http.MethodGet, url, nil)
  1591  				must.NoError(t, err)
  1592  				respW := httptest.NewRecorder()
  1593  
  1594  				// Ensure we have a token set.
  1595  				setToken(req, srv.RootToken)
  1596  
  1597  				// Send the HTTP request.
  1598  				obj, err := srv.Server.ACLBindingRuleSpecificRequest(respW, req)
  1599  				must.NoError(t, err)
  1600  
  1601  				result := obj.(*structs.ACLBindingRule)
  1602  				must.Eq(t, aclBindingRules[0].ID, result.ID)
  1603  				must.Eq(t, aclBindingRules[0].Selector, result.Selector)
  1604  				must.Eq(t, aclBindingRules[0].AuthMethod, result.AuthMethod)
  1605  				must.Eq(t, aclBindingRules[0].Description, result.Description)
  1606  			},
  1607  		},
  1608  		{
  1609  			name: "get, update, and delete binding rule",
  1610  			testFn: func(srv *TestAgent) {
  1611  
  1612  				// Upsert the auth method that the binding rule will associate
  1613  				// with.
  1614  				mockACLAuthMethod := mock.ACLOIDCAuthMethod()
  1615  				must.NoError(t, srv.server.State().UpsertACLAuthMethods(
  1616  					10, []*structs.ACLAuthMethod{mockACLAuthMethod}))
  1617  
  1618  				// Upsert a binding rule into state.
  1619  				mockedAclBindingRule := mock.ACLBindingRule()
  1620  				mockedAclBindingRule.AuthMethod = mockACLAuthMethod.Name
  1621  
  1622  				must.NoError(t, srv.server.State().UpsertACLBindingRules(
  1623  					10, []*structs.ACLBindingRule{mockedAclBindingRule}, true))
  1624  
  1625  				url := "/v1/acl/binding-rule/" + mockedAclBindingRule.ID
  1626  
  1627  				// Build the HTTP request to read the binding rule.
  1628  				req, err := http.NewRequest(http.MethodGet, url, nil)
  1629  				must.NoError(t, err)
  1630  				respW := httptest.NewRecorder()
  1631  
  1632  				// Ensure we have a token set.
  1633  				setToken(req, srv.RootToken)
  1634  
  1635  				// Send the HTTP request.
  1636  				obj, err := srv.Server.ACLBindingRuleSpecificRequest(respW, req)
  1637  				must.NoError(t, err)
  1638  
  1639  				result := obj.(*structs.ACLBindingRule)
  1640  				must.Eq(t, mockedAclBindingRule.ID, result.ID)
  1641  				must.Eq(t, mockedAclBindingRule.Selector, result.Selector)
  1642  				must.Eq(t, mockedAclBindingRule.AuthMethod, result.AuthMethod)
  1643  				must.Eq(t, mockedAclBindingRule.Description, result.Description)
  1644  
  1645  				// Update the binding rule and make the request via the HTTP
  1646  				// API.
  1647  				result.Description = "new description"
  1648  
  1649  				req, err = http.NewRequest(http.MethodPost, url, encodeReq(result))
  1650  				must.NoError(t, err)
  1651  				respW = httptest.NewRecorder()
  1652  
  1653  				// Ensure we have a token set.
  1654  				setToken(req, srv.RootToken)
  1655  
  1656  				// Send the HTTP request.
  1657  				_, err = srv.Server.ACLBindingRuleSpecificRequest(respW, req)
  1658  				must.NoError(t, err)
  1659  
  1660  				// Delete the ACL binding rule.
  1661  				req, err = http.NewRequest(http.MethodDelete, url, nil)
  1662  				must.NoError(t, err)
  1663  				respW = httptest.NewRecorder()
  1664  
  1665  				// Ensure we have a token set.
  1666  				setToken(req, srv.RootToken)
  1667  
  1668  				// Send the HTTP request.
  1669  				obj, err = srv.Server.ACLBindingRuleSpecificRequest(respW, req)
  1670  				must.NoError(t, err)
  1671  				must.Nil(t, obj)
  1672  
  1673  				// Ensure the ACL binding rule is no longer stored within
  1674  				// state.
  1675  				aclBindingRule, err := srv.server.State().GetACLBindingRule(nil, mockedAclBindingRule.ID)
  1676  				must.NoError(t, err)
  1677  				must.Nil(t, aclBindingRule)
  1678  			},
  1679  		},
  1680  	}
  1681  
  1682  	for _, tc := range testCases {
  1683  		t.Run(tc.name, func(t *testing.T) {
  1684  			cb := func(c *Config) { c.NomadConfig.ACLTokenMaxExpirationTTL = 3600 * time.Hour }
  1685  			httpACLTest(t, cb, tc.testFn)
  1686  		})
  1687  	}
  1688  }
  1689  
  1690  func TestHTTPServer_ACLOIDCAuthURLRequest(t *testing.T) {
  1691  	ci.Parallel(t)
  1692  
  1693  	testCases := []struct {
  1694  		name   string
  1695  		testFn func(srv *TestAgent)
  1696  	}{
  1697  		{
  1698  			name: "incorrect method",
  1699  			testFn: func(testAgent *TestAgent) {
  1700  
  1701  				// Build the HTTP request.
  1702  				req, err := http.NewRequest(http.MethodConnect, "/v1/acl/oidc/auth-url", nil)
  1703  				must.NoError(t, err)
  1704  				respW := httptest.NewRecorder()
  1705  
  1706  				// Send the HTTP request.
  1707  				obj, err := testAgent.Server.ACLOIDCAuthURLRequest(respW, req)
  1708  				must.Error(t, err)
  1709  				must.StrContains(t, err.Error(), "Invalid method")
  1710  				must.Nil(t, obj)
  1711  			},
  1712  		},
  1713  		{
  1714  			name: "success",
  1715  			testFn: func(testAgent *TestAgent) {
  1716  
  1717  				// Set up the test OIDC provider.
  1718  				oidcTestProvider := capOIDC.StartTestProvider(t)
  1719  				defer oidcTestProvider.Stop()
  1720  
  1721  				// Generate and upsert an ACL auth method for use. Certain values must be
  1722  				// taken from the cap OIDC provider just like real world use.
  1723  				mockedAuthMethod := mock.ACLOIDCAuthMethod()
  1724  				mockedAuthMethod.Config.AllowedRedirectURIs = []string{"http://127.0.0.1:4649/oidc/callback"}
  1725  				mockedAuthMethod.Config.OIDCDiscoveryURL = oidcTestProvider.Addr()
  1726  				mockedAuthMethod.Config.SigningAlgs = []string{"ES256"}
  1727  				mockedAuthMethod.Config.DiscoveryCaPem = []string{oidcTestProvider.CACert()}
  1728  
  1729  				must.NoError(t, testAgent.server.State().UpsertACLAuthMethods(
  1730  					10, []*structs.ACLAuthMethod{mockedAuthMethod}))
  1731  
  1732  				// Generate the request body.
  1733  				requestBody := structs.ACLOIDCAuthURLRequest{
  1734  					AuthMethodName: mockedAuthMethod.Name,
  1735  					RedirectURI:    mockedAuthMethod.Config.AllowedRedirectURIs[0],
  1736  					ClientNonce:    "fpSPuaodKevKfDU3IeXa",
  1737  					WriteRequest: structs.WriteRequest{
  1738  						Region: "global",
  1739  					},
  1740  				}
  1741  
  1742  				// Build the HTTP request.
  1743  				req, err := http.NewRequest(http.MethodPost, "/v1/acl/oidc/auth-url", encodeReq(&requestBody))
  1744  				must.NoError(t, err)
  1745  				respW := httptest.NewRecorder()
  1746  
  1747  				// Send the HTTP request.
  1748  				obj, err := testAgent.Server.ACLOIDCAuthURLRequest(respW, req)
  1749  				must.NoError(t, err)
  1750  
  1751  				// The response URL comes encoded, so decode this and check we have each
  1752  				// component we expect.
  1753  				escapedURL, err := url.PathUnescape(obj.(structs.ACLOIDCAuthURLResponse).AuthURL)
  1754  				must.NoError(t, err)
  1755  				must.StrContains(t, escapedURL, "/authorize?client_id=mock")
  1756  				must.StrContains(t, escapedURL, "&nonce=fpSPuaodKevKfDU3IeXa")
  1757  				must.StrContains(t, escapedURL, "&redirect_uri=http://127.0.0.1:4649/oidc/callback")
  1758  				must.StrContains(t, escapedURL, "&response_type=code")
  1759  				must.StrContains(t, escapedURL, "&scope=openid")
  1760  				must.StrContains(t, escapedURL, "&state=st_")
  1761  			},
  1762  		},
  1763  	}
  1764  
  1765  	for _, tc := range testCases {
  1766  		t.Run(tc.name, func(t *testing.T) {
  1767  			httpACLTest(t, nil, tc.testFn)
  1768  		})
  1769  	}
  1770  }
  1771  
  1772  func TestHTTPServer_ACLOIDCCompleteAuthRequest(t *testing.T) {
  1773  	ci.Parallel(t)
  1774  
  1775  	testCases := []struct {
  1776  		name   string
  1777  		testFn func(srv *TestAgent)
  1778  	}{
  1779  		{
  1780  			name: "incorrect method",
  1781  			testFn: func(testAgent *TestAgent) {
  1782  
  1783  				// Build the HTTP request.
  1784  				req, err := http.NewRequest(http.MethodConnect, "/v1/acl/oidc/complete-auth", nil)
  1785  				must.NoError(t, err)
  1786  				respW := httptest.NewRecorder()
  1787  
  1788  				// Send the HTTP request.
  1789  				obj, err := testAgent.Server.ACLOIDCCompleteAuthRequest(respW, req)
  1790  				must.Error(t, err)
  1791  				must.StrContains(t, err.Error(), "Invalid method")
  1792  				must.Nil(t, obj)
  1793  			},
  1794  		},
  1795  		{
  1796  			name: "success",
  1797  			testFn: func(testAgent *TestAgent) {
  1798  
  1799  				// Set up the test OIDC provider.
  1800  				oidcTestProvider := capOIDC.StartTestProvider(t)
  1801  				defer oidcTestProvider.Stop()
  1802  				oidcTestProvider.SetAllowedRedirectURIs([]string{"http://127.0.0.1:4649/oidc/callback"})
  1803  
  1804  				// Generate and upsert an ACL auth method for use. Certain values must be
  1805  				// taken from the cap OIDC provider just like real world use.
  1806  				mockedAuthMethod := mock.ACLOIDCAuthMethod()
  1807  				mockedAuthMethod.Config.BoundAudiences = []string{"mock"}
  1808  				mockedAuthMethod.Config.AllowedRedirectURIs = []string{"http://127.0.0.1:4649/oidc/callback"}
  1809  				mockedAuthMethod.Config.OIDCDiscoveryURL = oidcTestProvider.Addr()
  1810  				mockedAuthMethod.Config.SigningAlgs = []string{"ES256"}
  1811  				mockedAuthMethod.Config.DiscoveryCaPem = []string{oidcTestProvider.CACert()}
  1812  				mockedAuthMethod.Config.ClaimMappings = map[string]string{}
  1813  				mockedAuthMethod.Config.ListClaimMappings = map[string]string{
  1814  					"http://nomad.internal/roles":    "roles",
  1815  					"http://nomad.internal/policies": "policies",
  1816  				}
  1817  
  1818  				must.NoError(t, testAgent.server.State().UpsertACLAuthMethods(
  1819  					10, []*structs.ACLAuthMethod{mockedAuthMethod}))
  1820  
  1821  				// Set our custom data and some expected values, so we can make the RPC and
  1822  				// use the test provider.
  1823  				oidcTestProvider.SetExpectedAuthNonce("fpSPuaodKevKfDU3IeXa")
  1824  				oidcTestProvider.SetExpectedAuthCode("codeABC")
  1825  				oidcTestProvider.SetCustomAudience("mock")
  1826  				oidcTestProvider.SetExpectedState("st_someweirdstateid")
  1827  				oidcTestProvider.SetCustomClaims(map[string]interface{}{
  1828  					"azp":                            "mock",
  1829  					"http://nomad.internal/policies": []string{"engineering"},
  1830  					"http://nomad.internal/roles":    []string{"engineering"},
  1831  				})
  1832  
  1833  				// Generate the request body.
  1834  				requestBody := structs.ACLOIDCCompleteAuthRequest{
  1835  					AuthMethodName: mockedAuthMethod.Name,
  1836  					ClientNonce:    "fpSPuaodKevKfDU3IeXa",
  1837  					State:          "st_someweirdstateid",
  1838  					Code:           "codeABC",
  1839  					RedirectURI:    mockedAuthMethod.Config.AllowedRedirectURIs[0],
  1840  					WriteRequest: structs.WriteRequest{
  1841  						Region: "global",
  1842  					},
  1843  				}
  1844  
  1845  				// Build the HTTP request.
  1846  				req, err := http.NewRequest(http.MethodPost, "/v1/acl/oidc/complete-auth", encodeReq(&requestBody))
  1847  				must.NoError(t, err)
  1848  				respW := httptest.NewRecorder()
  1849  
  1850  				// Send the HTTP request.
  1851  				_, err = testAgent.Server.ACLOIDCCompleteAuthRequest(respW, req)
  1852  				must.ErrorContains(t, err, "no role or policy bindings matched")
  1853  
  1854  				// Upsert an ACL policy and role, so that we can reference this within our
  1855  				// OIDC claims.
  1856  				mockACLPolicy := mock.ACLPolicy()
  1857  				must.NoError(t, testAgent.server.State().UpsertACLPolicies(
  1858  					structs.MsgTypeTestSetup, 20, []*structs.ACLPolicy{mockACLPolicy}))
  1859  
  1860  				mockACLRole := mock.ACLRole()
  1861  				mockACLRole.Policies = []*structs.ACLRolePolicyLink{{Name: mockACLPolicy.Name}}
  1862  				must.NoError(t, testAgent.server.State().UpsertACLRoles(
  1863  					structs.MsgTypeTestSetup, 30, []*structs.ACLRole{mockACLRole}, true))
  1864  
  1865  				// Generate and upsert two binding rules, so we can test both ACL Policy
  1866  				// and Role claim mapping.
  1867  				mockBindingRule1 := mock.ACLBindingRule()
  1868  				mockBindingRule1.AuthMethod = mockedAuthMethod.Name
  1869  				mockBindingRule1.BindType = structs.ACLBindingRuleBindTypePolicy
  1870  				mockBindingRule1.Selector = "engineering in list.policies"
  1871  				mockBindingRule1.BindName = mockACLPolicy.Name
  1872  
  1873  				mockBindingRule2 := mock.ACLBindingRule()
  1874  				mockBindingRule2.AuthMethod = mockedAuthMethod.Name
  1875  				mockBindingRule2.BindName = mockACLRole.Name
  1876  
  1877  				must.NoError(t, testAgent.server.State().UpsertACLBindingRules(
  1878  					40, []*structs.ACLBindingRule{mockBindingRule1, mockBindingRule2}, true))
  1879  
  1880  				// Build the HTTP request.
  1881  				req, err = http.NewRequest(http.MethodPost, "/v1/acl/oidc/complete-auth", encodeReq(&requestBody))
  1882  				must.NoError(t, err)
  1883  				respW = httptest.NewRecorder()
  1884  
  1885  				// Send the HTTP request.
  1886  				obj, err := testAgent.Server.ACLOIDCCompleteAuthRequest(respW, req)
  1887  				must.NoError(t, err)
  1888  
  1889  				aclTokenResp, ok := obj.(*structs.ACLToken)
  1890  				must.True(t, ok)
  1891  				must.NotNil(t, aclTokenResp)
  1892  				must.Len(t, 1, aclTokenResp.Policies)
  1893  				must.Eq(t, mockACLPolicy.Name, aclTokenResp.Policies[0])
  1894  				must.Len(t, 1, aclTokenResp.Roles)
  1895  				must.Eq(t, mockACLRole.Name, aclTokenResp.Roles[0].Name)
  1896  				must.Eq(t, mockACLRole.ID, aclTokenResp.Roles[0].ID)
  1897  			},
  1898  		},
  1899  	}
  1900  
  1901  	for _, tc := range testCases {
  1902  		t.Run(tc.name, func(t *testing.T) {
  1903  			httpACLTest(t, nil, tc.testFn)
  1904  		})
  1905  	}
  1906  }
  1907  
  1908  func TestHTTPServer_ACLLoginRequest(t *testing.T) {
  1909  	ci.Parallel(t)
  1910  
  1911  	testCases := []struct {
  1912  		name   string
  1913  		testFn func(srv *TestAgent)
  1914  	}{
  1915  		{
  1916  			name: "incorrect method",
  1917  			testFn: func(testAgent *TestAgent) {
  1918  
  1919  				// Build the HTTP request.
  1920  				req, err := http.NewRequest(http.MethodConnect, "/v1/acl/login", nil)
  1921  				must.NoError(t, err)
  1922  				respW := httptest.NewRecorder()
  1923  
  1924  				// Send the HTTP request.
  1925  				obj, err := testAgent.Server.ACLOIDCCompleteAuthRequest(respW, req)
  1926  				must.Error(t, err)
  1927  				must.StrContains(t, err.Error(), "Invalid method")
  1928  				must.Nil(t, obj)
  1929  			},
  1930  		},
  1931  		{
  1932  			name: "success",
  1933  			testFn: func(testAgent *TestAgent) {
  1934  
  1935  				// Generate a sample JWT
  1936  				iat := time.Now().Unix()
  1937  				nbf := time.Now().Unix()
  1938  				exp := time.Now().Add(time.Hour).Unix()
  1939  				claims := jwt.MapClaims{
  1940  					"iss":                            "nomad test suite",
  1941  					"iat":                            iat,
  1942  					"nbf":                            nbf,
  1943  					"exp":                            exp,
  1944  					"aud":                            "engineering",
  1945  					"http://nomad.internal/policies": []string{"engineering"},
  1946  					"http://nomad.internal/roles":    []string{"engineering"},
  1947  				}
  1948  
  1949  				token, pubKey, err := mock.SampleJWTokenWithKeys(claims, nil)
  1950  				must.NoError(t, err)
  1951  
  1952  				// Generate and upsert a JWT ACL auth method for use.
  1953  				mockedAuthMethod := mock.ACLJWTAuthMethod()
  1954  				mockedAuthMethod.Config.BoundAudiences = []string{"engineering"}
  1955  				mockedAuthMethod.Config.JWTValidationPubKeys = []string{pubKey}
  1956  				mockedAuthMethod.Config.BoundIssuer = []string{"nomad test suite"}
  1957  				mockedAuthMethod.Config.ExpirationLeeway = time.Duration(3600)
  1958  				mockedAuthMethod.Config.ClockSkewLeeway = time.Duration(3600)
  1959  				mockedAuthMethod.Config.ClaimMappings = map[string]string{}
  1960  				mockedAuthMethod.Config.ListClaimMappings = map[string]string{
  1961  					"http://nomad.internal/roles":    "roles",
  1962  					"http://nomad.internal/policies": "policies",
  1963  				}
  1964  
  1965  				must.NoError(t, testAgent.server.State().UpsertACLAuthMethods(
  1966  					10, []*structs.ACLAuthMethod{mockedAuthMethod}))
  1967  
  1968  				// Generate the request body.
  1969  				requestBody := structs.ACLLoginRequest{
  1970  					AuthMethodName: mockedAuthMethod.Name,
  1971  					LoginToken:     token,
  1972  					WriteRequest: structs.WriteRequest{
  1973  						Region: "global",
  1974  					},
  1975  				}
  1976  
  1977  				// Build the HTTP request.
  1978  				req, err := http.NewRequest(http.MethodPost, "/v1/acl/login", encodeReq(&requestBody))
  1979  				must.NoError(t, err)
  1980  				respW := httptest.NewRecorder()
  1981  
  1982  				// Send the HTTP request.
  1983  				_, err = testAgent.Server.ACLLoginRequest(respW, req)
  1984  				must.ErrorContains(t, err, "no role or policy bindings matched")
  1985  
  1986  				// Upsert an ACL policy and role, so that we can reference this within our
  1987  				// OIDC claims.
  1988  				mockACLPolicy := mock.ACLPolicy()
  1989  				must.NoError(t, testAgent.server.State().UpsertACLPolicies(
  1990  					structs.MsgTypeTestSetup, 20, []*structs.ACLPolicy{mockACLPolicy}))
  1991  
  1992  				mockACLRole := mock.ACLRole()
  1993  				mockACLRole.Policies = []*structs.ACLRolePolicyLink{{Name: mockACLPolicy.Name}}
  1994  				must.NoError(t, testAgent.server.State().UpsertACLRoles(
  1995  					structs.MsgTypeTestSetup, 30, []*structs.ACLRole{mockACLRole}, true))
  1996  
  1997  				// Generate and upsert two binding rules, so we can test both ACL Policy
  1998  				// and Role claim mapping.
  1999  				mockBindingRule1 := mock.ACLBindingRule()
  2000  				mockBindingRule1.AuthMethod = mockedAuthMethod.Name
  2001  				mockBindingRule1.BindType = structs.ACLBindingRuleBindTypePolicy
  2002  				mockBindingRule1.Selector = "engineering in list.policies"
  2003  				mockBindingRule1.BindName = mockACLPolicy.Name
  2004  
  2005  				mockBindingRule2 := mock.ACLBindingRule()
  2006  				mockBindingRule2.AuthMethod = mockedAuthMethod.Name
  2007  				mockBindingRule2.BindName = mockACLRole.Name
  2008  
  2009  				must.NoError(t, testAgent.server.State().UpsertACLBindingRules(
  2010  					40, []*structs.ACLBindingRule{mockBindingRule1, mockBindingRule2}, true))
  2011  
  2012  				// Build the HTTP request.
  2013  				req, err = http.NewRequest(http.MethodPost, "/v1/acl/login", encodeReq(&requestBody))
  2014  				must.NoError(t, err)
  2015  				respW = httptest.NewRecorder()
  2016  
  2017  				// Send the HTTP request.
  2018  				obj, err := testAgent.Server.ACLLoginRequest(respW, req)
  2019  				must.NoError(t, err)
  2020  
  2021  				aclTokenResp, ok := obj.(*structs.ACLToken)
  2022  				must.True(t, ok)
  2023  				must.NotNil(t, aclTokenResp)
  2024  				must.Len(t, 1, aclTokenResp.Policies)
  2025  				must.Eq(t, mockACLPolicy.Name, aclTokenResp.Policies[0])
  2026  				must.Len(t, 1, aclTokenResp.Roles)
  2027  				must.Eq(t, mockACLRole.Name, aclTokenResp.Roles[0].Name)
  2028  				must.Eq(t, mockACLRole.ID, aclTokenResp.Roles[0].ID)
  2029  			},
  2030  		},
  2031  	}
  2032  
  2033  	for _, tc := range testCases {
  2034  		t.Run(tc.name, func(t *testing.T) {
  2035  			httpACLTest(t, nil, tc.testFn)
  2036  		})
  2037  	}
  2038  }