Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for Security v2 API #754

Draft
wants to merge 2 commits into
base: dev
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
146 changes: 146 additions & 0 deletions access/services/v2/groups.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
package v2
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's rempve the v2 package. and have this file as well as users.go directly umder access/services.


import (
"encoding/json"
"fmt"
"github.com/jfrog/jfrog-client-go/auth"
"github.com/jfrog/jfrog-client-go/http/jfroghttpclient"
"github.com/jfrog/jfrog-client-go/utils/errorutils"
"github.com/jfrog/jfrog-client-go/utils/io/httputils"
"net/http"
)

const groupsApi = "api/v2/groups"

type GroupParams struct {
GroupDetails
}

type GroupDetails struct {
Name string `json:"name,omitempty"`
Description string `json:"description,omitempty"`
AutoJoin *bool `json:"auto_join,omitempty"`
AdminPrivileges *bool `json:"admin_privileges,omitempty"`
Realm string `json:"realm,omitempty"`
RealmAttributes string `json:"realm_attributes,omitempty"`
ExternalId string `json:"external_id,omitempty"`
Members []string `json:"members,omitempty"`
}

type GroupListItem struct {
GroupName string `json:"group_name"`
Uri string `json:"uri"`
}
type GroupList struct {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing new-line before type definition. There a few other places in the code added by this PR which have new-lines between functions / structs missing.

Cursor string `json:"cursor,omitempty"`
Groups []GroupListItem `json:"groups"`
}

func NewGroupParams() GroupParams {
return GroupParams{}
}

type GroupService struct {
client *jfroghttpclient.JfrogHttpClient
ServiceDetails auth.ServiceDetails
}

func NewGroupService(client *jfroghttpclient.JfrogHttpClient) *GroupService {
return &GroupService{client: client}
}

func (gs *GroupService) getBaseUrl() string {
return fmt.Sprintf("%s%s", gs.ServiceDetails.GetUrl(), groupsApi)
}
func (gs *GroupService) GetAll() ([]GroupListItem, error) {
httpDetails := gs.ServiceDetails.CreateHttpClientDetails()
url := gs.getBaseUrl()
resp, body, _, err := gs.client.SendGet(url, true, &httpDetails)
if err != nil {
return nil, err
}

if err = errorutils.CheckResponseStatusWithBody(resp, body, http.StatusOK); err != nil {
return nil, err
}
var groupList GroupList
err = json.Unmarshal(body, &groupList)
return groupList.Groups, errorutils.CheckError(err)
}

func (gs *GroupService) Get(name string) (u *GroupDetails, err error) {
httpDetails := gs.ServiceDetails.CreateHttpClientDetails()
url := fmt.Sprintf("%s/%s", gs.getBaseUrl(), name)
resp, body, _, err := gs.client.SendGet(url, true, &httpDetails)
if err != nil {
return nil, err
}

if resp.StatusCode == http.StatusNotFound {
return nil, nil
}
if err = errorutils.CheckResponseStatusWithBody(resp, body, http.StatusOK); err != nil {
return nil, err
}
var group GroupDetails
err = json.Unmarshal(body, &group)
return &group, errorutils.CheckError(err)
}

func (gs *GroupService) Create(params GroupParams) error {
group, err := gs.Get(params.Name)
if err != nil {
return err
}
if group != nil {
return errorutils.CheckErrorf("group '%s' already exists", group.Name)
}
content, httpDetails, err := gs.createOrUpdateRequest(params)
if err != nil {
return err
}
resp, body, err := gs.client.SendPost(gs.getBaseUrl(), content, &httpDetails)
if err != nil {
return err
}
return errorutils.CheckResponseStatusWithBody(resp, body, http.StatusCreated)
}

func (gs *GroupService) Update(params GroupParams) error {
content, httpDetails, err := gs.createOrUpdateRequest(params)
if err != nil {
return err
}
url := fmt.Sprintf("%s/%s", gs.getBaseUrl(), params.Name)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use path.Join in this case. The same goes for a few other places in this PR.

resp, body, err := gs.client.SendPatch(url, content, &httpDetails)
if err != nil {
return err
}
return errorutils.CheckResponseStatusWithBody(resp, body, http.StatusOK)
}

func (gs *GroupService) createOrUpdateRequest(group GroupParams) (requestContent []byte, httpDetails httputils.HttpClientDetails, err error) {
httpDetails = gs.ServiceDetails.CreateHttpClientDetails()
requestContent, err = json.Marshal(group)
if errorutils.CheckError(err) != nil {
return
}
httpDetails.Headers = map[string]string{
"Content-Type": "application/json",
"Accept": "application/json",
}
return
}

func (gs *GroupService) Delete(name string) error {
httpDetails := gs.ServiceDetails.CreateHttpClientDetails()
url := fmt.Sprintf("%s/%s", gs.getBaseUrl(), name)
resp, body, err := gs.client.SendDelete(url, nil, &httpDetails)
if err != nil {
return err
}
if resp == nil {
return errorutils.CheckErrorf("no response provided (including status code)")
}
return errorutils.CheckResponseStatusWithBody(resp, body, http.StatusNoContent)
}
145 changes: 145 additions & 0 deletions access/services/v2/users.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
package v2

import (
"encoding/json"
"fmt"
"net/http"

"github.com/jfrog/jfrog-client-go/auth"
"github.com/jfrog/jfrog-client-go/http/jfroghttpclient"
"github.com/jfrog/jfrog-client-go/utils/errorutils"
"github.com/jfrog/jfrog-client-go/utils/io/httputils"
)

const usersApi = "api/v2/users"

type UserParams struct {
CommonUserParams
}

type UserResponse struct {
CommonUserParams
Status string `json:"status,omitempty"`
}

func NewUserParams() UserParams {
return UserParams{}
}

type CommonUserParams struct {
Username string `json:"username,omitempty"`
Email string `json:"email,omitempty"`
Password string `json:"password,omitempty"`
Admin *bool `json:"admin,omitempty"`
ProfileUpdatable *bool `json:"profile_updatable,omitempty"`
DisableUIAccess *bool `json:"disable_ui_access,omitempty"`
InternalPasswordDisabled *bool `json:"internal_password_disabled,omitempty"`
Realm string `json:"realm,omitempty"`
Groups *[]string `json:"groups,omitempty"`
}

type UserService struct {
client *jfroghttpclient.JfrogHttpClient
ServiceDetails auth.ServiceDetails
}

func NewUserService(client *jfroghttpclient.JfrogHttpClient) *UserService {
return &UserService{client: client}
}

func (us *UserService) getBaseUrl() string {
return fmt.Sprintf("%s%s", us.ServiceDetails.GetUrl(), usersApi)
}

func (us *UserService) GetAll() ([]UserResponse, error) {
httpDetails := us.ServiceDetails.CreateHttpClientDetails()
url := us.getBaseUrl()
resp, body, _, err := us.client.SendGet(url, true, &httpDetails)
if err != nil {
return nil, err
}

if err = errorutils.CheckResponseStatusWithBody(resp, body, http.StatusOK); err != nil {
return nil, err
}
var users []UserResponse
err = json.Unmarshal(body, &users)
return users, errorutils.CheckError(err)
}

func (us *UserService) Get(username string) (u *UserResponse, err error) {
httpDetails := us.ServiceDetails.CreateHttpClientDetails()
url := fmt.Sprintf("%s/%s", us.getBaseUrl(), username)
resp, body, _, err := us.client.SendGet(url, true, &httpDetails)
if err != nil {
return nil, err
}

if resp.StatusCode == http.StatusNotFound {
return nil, nil
}
if err = errorutils.CheckResponseStatusWithBody(resp, body, http.StatusOK); err != nil {
return nil, err
}
var user UserResponse
err = json.Unmarshal(body, &user)
return &user, errorutils.CheckError(err)
}

func (us *UserService) Create(params UserParams) error {
user, err := us.Get(params.Username)
if err != nil {
return err
}
if user != nil {
return errorutils.CheckErrorf("user '%s' already exists", user.Username)
}
content, httpDetails, err := us.createOrUpdateRequest(params)
if err != nil {
return err
}
resp, body, err := us.client.SendPost(us.getBaseUrl(), content, &httpDetails)
if err != nil {
return err
}
return errorutils.CheckResponseStatusWithBody(resp, body, http.StatusCreated)
}

func (us *UserService) Update(params UserParams) error {
content, httpDetails, err := us.createOrUpdateRequest(params)
if err != nil {
return err
}
url := fmt.Sprintf("%s/%s", us.getBaseUrl(), params.Username)
resp, body, err := us.client.SendPatch(url, content, &httpDetails)
if err != nil {
return err
}
return errorutils.CheckResponseStatusWithBody(resp, body, http.StatusOK)
}

func (us *UserService) createOrUpdateRequest(user UserParams) (requestContent []byte, httpDetails httputils.HttpClientDetails, err error) {
httpDetails = us.ServiceDetails.CreateHttpClientDetails()
requestContent, err = json.Marshal(user)
if errorutils.CheckError(err) != nil {
return
}
httpDetails.Headers = map[string]string{
"Content-Type": "application/json",
"Accept": "application/json",
}
return
}

func (us *UserService) Delete(username string) error {
httpDetails := us.ServiceDetails.CreateHttpClientDetails()
url := fmt.Sprintf("%s/%s", us.getBaseUrl(), username)
resp, body, err := us.client.SendDelete(url, nil, &httpDetails)
if err != nil {
return err
}
if resp == nil {
return errorutils.CheckErrorf("no response provided (including status code)")
}
return errorutils.CheckResponseStatusWithBody(resp, body, http.StatusNoContent)
}
92 changes: 92 additions & 0 deletions tests/accessgroups_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package tests

import (
"fmt"
"testing"

services "github.com/jfrog/jfrog-client-go/access/services/v2"
"github.com/stretchr/testify/assert"
)

func TestAccessGroups(t *testing.T) {
initAccessTest(t)
t.Run("create", testCreateAccessGroup)
t.Run("update", testUpdateAccessGroup)
t.Run("delete", testDeleteAccessGroup)
}

func getTestAccessGroupParams() services.GroupParams {
group := services.GroupDetails{
Name: fmt.Sprintf("test-%s", getRunId()),
Description: "hello",
AutoJoin: &falseValue,
AdminPrivileges: &trueValue,
Realm: "internal",
RealmAttributes: "",
ExternalId: "",
}
return services.GroupParams{GroupDetails: group}
}

func testCreateAccessGroup(t *testing.T) {
groupParams := getTestAccessGroupParams()
err := testAccessGroupService.Create(groupParams)
defer deleteAccessGroupAndAssert(t, groupParams.GroupDetails.Name)
assert.NoError(t, err)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This error assertion should be placed right after the line that returns the error (before the defer).
The same goes for the testUpdateAccessGroup function.


createdGroup, err := testAccessGroupService.Get(groupParams.Name)
assert.NoError(t, err)
assert.NotNil(t, createdGroup)
assert.Equal(t, groupParams.GroupDetails, *createdGroup)

allGroups, err := testAccessGroupService.GetAll()
assert.NoError(t, err)
assert.NotNil(t, allGroups)

var groupNames []string
for _, v := range allGroups {
groupNames = append(groupNames, v.GroupName)
}
assert.Contains(t, groupNames, groupParams.GroupDetails.Name)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant new-line.

}

func testUpdateAccessGroup(t *testing.T) {
groupParams := getTestAccessGroupParams()
err := testAccessGroupService.Create(groupParams)
defer deleteAccessGroupAndAssert(t, groupParams.Name)
assert.NoError(t, err)
groupParams.Description = "Changed description"
groupParams.AutoJoin = &trueValue
groupParams.AdminPrivileges = &falseValue
err = testAccessGroupService.Update(groupParams)
assert.NoError(t, err)
group, err := testAccessGroupService.Get(groupParams.Name)
assert.NoError(t, err)
assert.Equal(t, groupParams.GroupDetails, *group)
}

func testDeleteAccessGroup(t *testing.T) {
groupParams := getTestAccessGroupParams()
assert.NoError(t, testAccessGroupService.Create(groupParams))

deleteAccessGroupAndAssert(t, groupParams.Name)

group, err := testAccessGroupService.Get(groupParams.Name)
assert.NoError(t, err)
assert.Nil(t, group)

allGroups, err := testAccessGroupService.GetAll()
assert.NoError(t, err)
assert.NotNil(t, allGroups)

var allGroupNames []string
for _, v := range allGroups {
allGroupNames = append(allGroupNames, v.GroupName)
}
assert.NotContains(t, allGroupNames, groupParams.Name)
}

func deleteAccessGroupAndAssert(t *testing.T, groupName string) {
assert.NoError(t, testAccessGroupService.Delete(groupName))
}