Auth: Implement the SSO Settings List endpoint (#80769)
* add list endpoint & initial tests * add tests and ETag * format service_test.go * add list swagger param, generate openAPI, remove ETag, use RedactedPassword * correct swagger param name * Align tests to latest changes * use setting.RedactedValue() * add string assertion * lint & require no error on res.Body.Close() * add custom response type --------- Co-authored-by: Mihaly Gyongyosi <mgyongyosi@users.noreply.github.com>
This commit is contained in:
co-authored by
Mihaly Gyongyosi
parent
40312c527b
commit
e241188f00
@@ -5,6 +5,7 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -367,6 +368,174 @@ func TestSSOSettingsAPI_GetForProvider(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSSOSettingsAPI_List(t *testing.T) {
|
||||
type TestCase struct {
|
||||
desc string
|
||||
action string
|
||||
scope string
|
||||
expectedResult []*models.SSOSettings
|
||||
errFromService error
|
||||
wantErr bool
|
||||
expectedErrMessage string
|
||||
expectedServiceCall bool
|
||||
expectedStatusCode int
|
||||
}
|
||||
|
||||
tests := []TestCase{
|
||||
{
|
||||
desc: "successfully lists SSO settings",
|
||||
action: "settings:read",
|
||||
scope: "settings:auth.azuread:*",
|
||||
expectedResult: []*models.SSOSettings{
|
||||
{
|
||||
ID: "1",
|
||||
Provider: "azuread",
|
||||
Settings: make(map[string]interface{}),
|
||||
Source: models.DB,
|
||||
},
|
||||
},
|
||||
expectedServiceCall: true,
|
||||
expectedStatusCode: http.StatusOK,
|
||||
},
|
||||
{
|
||||
desc: "returns empty list when the user has the action but the scope doesn't match any of the providerss scope",
|
||||
action: "settings:read",
|
||||
scope: "settings:auth.saml:write",
|
||||
expectedResult: []*models.SSOSettings{},
|
||||
expectedServiceCall: true,
|
||||
expectedStatusCode: http.StatusOK,
|
||||
},
|
||||
{
|
||||
desc: "successfully lists SSO settings when scope contains wildcard",
|
||||
action: "settings:read",
|
||||
scope: "settings:*",
|
||||
expectedResult: []*models.SSOSettings{
|
||||
{
|
||||
ID: "1",
|
||||
Provider: "azuread",
|
||||
Settings: make(map[string]interface{}),
|
||||
Source: models.DB,
|
||||
},
|
||||
{
|
||||
ID: "2",
|
||||
Provider: "github",
|
||||
Settings: make(map[string]interface{}),
|
||||
Source: models.DB,
|
||||
},
|
||||
{
|
||||
ID: "3",
|
||||
Provider: "okta",
|
||||
Settings: make(map[string]interface{}),
|
||||
Source: models.System,
|
||||
},
|
||||
},
|
||||
expectedServiceCall: true,
|
||||
expectedStatusCode: http.StatusOK,
|
||||
},
|
||||
{
|
||||
desc: "fails when action doesn't match",
|
||||
action: "madeupaction:read",
|
||||
scope: "madeupscope:*",
|
||||
wantErr: true,
|
||||
expectedErrMessage: "You'll need additional permissions to perform this action. Permissions needed: settings:read",
|
||||
expectedResult: nil,
|
||||
expectedServiceCall: false,
|
||||
expectedStatusCode: http.StatusForbidden,
|
||||
},
|
||||
{
|
||||
desc: "fails with internal server error when service returns an error",
|
||||
action: "settings:read",
|
||||
scope: "settings:auth.azuread:*",
|
||||
errFromService: errors.New("something went wrong"),
|
||||
expectedResult: nil,
|
||||
wantErr: true,
|
||||
expectedErrMessage: "Failed to list all providers settings",
|
||||
expectedServiceCall: true,
|
||||
expectedStatusCode: http.StatusInternalServerError,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.desc, func(t *testing.T) {
|
||||
service := ssosettingstests.NewMockService(t)
|
||||
|
||||
serviceResult := []*models.SSOSettings{
|
||||
{
|
||||
ID: "1",
|
||||
Provider: "azuread",
|
||||
Settings: make(map[string]interface{}),
|
||||
Created: time.Now(),
|
||||
Updated: time.Now(),
|
||||
IsDeleted: false,
|
||||
Source: models.DB,
|
||||
},
|
||||
{
|
||||
ID: "2",
|
||||
Provider: "github",
|
||||
Settings: make(map[string]interface{}),
|
||||
Created: time.Now(),
|
||||
Updated: time.Now(),
|
||||
IsDeleted: false,
|
||||
Source: models.DB,
|
||||
},
|
||||
{
|
||||
ID: "3",
|
||||
Provider: "okta",
|
||||
Settings: make(map[string]interface{}),
|
||||
Created: time.Now(),
|
||||
Updated: time.Now(),
|
||||
IsDeleted: false,
|
||||
Source: models.System,
|
||||
},
|
||||
}
|
||||
if tt.expectedServiceCall {
|
||||
service.On("ListWithRedactedSecrets", mock.AnythingOfType("*context.valueCtx")).Return(serviceResult, tt.errFromService).Once()
|
||||
}
|
||||
server := setupTests(t, service)
|
||||
|
||||
path := "/api/v1/sso-settings"
|
||||
req := server.NewRequest(http.MethodGet, path, nil)
|
||||
webtest.RequestWithSignedInUser(req, &user.SignedInUser{
|
||||
OrgRole: org.RoleEditor,
|
||||
OrgID: 1,
|
||||
Permissions: getPermissionsForActionAndScope(tt.action, tt.scope),
|
||||
})
|
||||
res, err := server.SendJSON(req)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, tt.expectedStatusCode, res.StatusCode)
|
||||
|
||||
bodyBytes, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to read response body: %v", err)
|
||||
}
|
||||
|
||||
if tt.wantErr {
|
||||
var accessErrorResponse struct {
|
||||
AccessErrorID string `json:"accessErrorId"`
|
||||
Message string `json:"message"`
|
||||
Title string `json:"title"`
|
||||
}
|
||||
err = json.Unmarshal(bodyBytes, &accessErrorResponse)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to unmarshal response body into accessErrorResponse: %v", err)
|
||||
}
|
||||
|
||||
require.Equal(t, tt.expectedErrMessage, accessErrorResponse.Message)
|
||||
return
|
||||
}
|
||||
|
||||
var actual []*models.SSOSettings
|
||||
err = json.Unmarshal(bodyBytes, &actual)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.ElementsMatch(t, tt.expectedResult, actual)
|
||||
err = res.Body.Close()
|
||||
require.NoError(t, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func getPermissionsForActionAndScope(action, scope string) map[int64]map[string][]string {
|
||||
return map[int64]map[string][]string{
|
||||
1: accesscontrol.GroupScopesByAction([]accesscontrol.Permission{{
|
||||
|
||||
Reference in New Issue
Block a user