-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Feature/17 implement service accounts (#23)
* chore(serviceaccount): List and Create service accounts * chore(serviceaccount): Add service to Keyhub class * Added update, get and delete functions * chore(serviceaccount): Add ServiceAccountQueryParams * chore(test): add some queryParam marshal testing * chore(serviceaccounts): Added remaining query params
- Loading branch information
1 parent
0b64ad7
commit 7dad85b
Showing
5 changed files
with
362 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,107 @@ | ||
package model | ||
|
||
import ( | ||
"github.com/google/uuid" | ||
"net/url" | ||
"time" | ||
) | ||
|
||
const ( | ||
SA_PASSWORD_ROTATION_MANUAL SAPasswordRotation = "MANUAL" | ||
SA_PASSWORD_ROTATION_MANUAL_SIV SAPasswordRotation = "MANUAL_STORED_IN_VAULT" | ||
SA_PASSWORD_ROTATION_DAILY SAPasswordRotation = "DAILY" | ||
) | ||
|
||
type SAPasswordRotation string | ||
|
||
type ServiceAccountList struct { | ||
Items []ServiceAccount `json:"items"` | ||
} | ||
|
||
type ServiceAccountPrimer struct { | ||
//#/components/schemas/serviceaccount_ServiceAccountPrimer | ||
//serviceaccount_ServiceAccount | ||
|
||
Linkable | ||
|
||
Active bool `json:"active"` | ||
UUID uuid.UUID `json:"uuid,omitempty"` | ||
Name string `json:"name"` | ||
Username string `json:"username"` | ||
System *ProvisionedSystem `json:"system"` | ||
} | ||
|
||
type ServiceAccount struct { | ||
ServiceAccountPrimer | ||
|
||
TechnicalAdministrator *Group `json:"technicalAdministrator,omitempty"` | ||
PasswordRotation SAPasswordRotation `json:"passwordRotation"` | ||
Description string `json:"description,omitempty"` | ||
Password *VaultRecord `json:"password,omitempty"` | ||
} | ||
|
||
// AsPrimer Return ServiceAccount with only Primer data | ||
func (s *ServiceAccount) AsPrimer() *ServiceAccount { | ||
serviceAccount := &ServiceAccount{} | ||
serviceAccount.ServiceAccountPrimer = s.ServiceAccountPrimer | ||
return serviceAccount | ||
} | ||
|
||
// ToPrimer Convert to serviceAccountPrimer | ||
func (s *ServiceAccount) ToPrimer() *ServiceAccountPrimer { | ||
serviceAccountPrimer := s.ServiceAccountPrimer | ||
return &serviceAccountPrimer | ||
} | ||
|
||
type ServiceAccountGroupList struct { | ||
Items []ServiceAccountGroup `json:"items"` | ||
} | ||
|
||
type ServiceAccountGroup struct { | ||
GroupOnSystem | ||
} | ||
|
||
type ServiceAccountQueryParams struct { | ||
UUID string `url:"uuid,omitempty"` | ||
CreatedAfter time.Time `url:"createdAfter,omitempty" layout:"2006-01-02T15:04:05Z"` | ||
CreatedBefore time.Time `url:"createdBefore,omitempty" layout:"2006-01-02T15:04:05Z"` | ||
ModifiedSince time.Time `url:"createdBefore,omitempty" layout:"2006-01-02T15:04:05Z"` | ||
Additional *ServiceAccountAdditionalQueryParams `url:"additional,omitempty"` | ||
Exclude []int64 `url:"exclude,omitempty"` | ||
Id []int64 `url:"id,omitempty"` | ||
CQLQuery string `url:"q,omitempty"` | ||
Active string `url:"active,omitempty"` | ||
GroupOnSystem int64 `url:"groupOnSystem,omitempty"` | ||
GroupOnSystemOwners int64 `url:"groupOnSystemOwners,omitempty"` | ||
Name string `url:"name,omitempty"` | ||
NameContains string `url:"nameContains,omitempty"` | ||
NameDoesNotStartWith string `url:"nameDoesNotStartWith,omitempty"` | ||
NameStartsWith string `url:"nameStartsWith,omitempty"` | ||
Password int64 `url:"password,omitempty"` | ||
PasswordRotation string `url:"passwordRotation,omitempty"` | ||
RequestedGroupOnSystemOwners int64 `url:"requestedGroupOnSystemOwners,omitempty"` | ||
System int64 `url:"system,omitempty"` | ||
TechnicalAdministrator int64 `url:"technicalAdministrator,omitempty"` | ||
Username string `url:"Username,omitempty"` | ||
} | ||
|
||
type ServiceAccountAdditionalQueryParams struct { | ||
Audit bool `url:"audit"` | ||
Groups bool `url:"groups"` | ||
Secrets bool `url:"secrets"` | ||
} | ||
|
||
// EncodeValues Custom url encoder to convert bools to list | ||
func (p ServiceAccountAdditionalQueryParams) EncodeValues(key string, v *url.Values) error { | ||
return additionalQueryParamsUrlEncoder(p, key, v) | ||
} | ||
|
||
func NewServiceAccount() *ServiceAccount { | ||
|
||
return &ServiceAccount{ | ||
ServiceAccountPrimer: ServiceAccountPrimer{ | ||
Linkable: Linkable{DType: "serviceaccount.ServiceAccount"}, | ||
}, | ||
PasswordRotation: SA_PASSWORD_ROTATION_MANUAL, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,195 @@ | ||
/* Licensed to the Apache Software Foundation (ASF) under one or more | ||
contributor license agreements. See the NOTICE file distributed with | ||
this work for additional information regarding copyright ownership. | ||
The ASF licenses this file to You under the Apache License, Version 2.0 | ||
(the "License"); you may not use this file except in compliance with | ||
the License. You may obtain a copy of the License at | ||
http://www.apache.org/licenses/LICENSE-2.0 | ||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. */ | ||
|
||
package keyhub | ||
|
||
import ( | ||
"fmt" | ||
"github.com/dghubble/sling" | ||
"github.com/google/uuid" | ||
"github.com/topicuskeyhub/go-keyhub/model" | ||
"net/http" | ||
"net/url" | ||
"strconv" | ||
) | ||
|
||
type ServiceAccountService struct { | ||
sling *sling.Sling | ||
} | ||
|
||
// NewServiceAccountService Create new ServiceAccountService | ||
func NewServiceAccountService(sling *sling.Sling) *ServiceAccountService { | ||
return &ServiceAccountService{ | ||
sling: sling.Path("/keyhub/rest/v1/serviceaccount/"), | ||
} | ||
} | ||
|
||
// GetByUUID Get Service account by UUID | ||
func (s *ServiceAccountService) GetByUUID(uuid uuid.UUID) (result *model.ServiceAccount, err error) { | ||
list := new(model.ServiceAccountList) | ||
errorReport := new(model.ErrorReport) | ||
|
||
params := &model.ServiceAccountQueryParams{UUID: uuid.String()} | ||
|
||
_, err = s.sling.New().Get("").QueryStruct(params).Receive(list, errorReport) | ||
if errorReport.Code > 0 { | ||
err = errorReport.Wrap("Could not get ServiceAccount %q.", uuid) | ||
} | ||
if err == nil { | ||
if len(list.Items) > 0 { | ||
result = &list.Items[0] | ||
} else { | ||
err = fmt.Errorf("Account %q not found", uuid.String()) | ||
} | ||
} | ||
|
||
return | ||
} | ||
|
||
// GetById Get Service account by ID | ||
func (s *ServiceAccountService) GetById(id int64) (result *model.ServiceAccount, err error) { | ||
sa := new(model.ServiceAccount) | ||
errorReport := new(model.ErrorReport) | ||
idString := strconv.FormatInt(id, 10) | ||
|
||
_, err = s.sling.New().Get(idString).Receive(sa, errorReport) | ||
if errorReport.Code > 0 { | ||
err = errorReport.Wrap("Could not get ServiceAccount %q.", idString) | ||
return | ||
} | ||
if err == nil && sa == nil { | ||
err = fmt.Errorf("Account %q not found", idString) | ||
return | ||
} | ||
|
||
//use an intermediate variable so sling can fill that variable with the json results. When request was succesful we use the variable as return value. | ||
result = sa | ||
return | ||
} | ||
|
||
//// List Retrieve all vault records for a group (secrets are not included, default audit = true) | ||
//func (s *VaultService) List(group *model.Group, query *model.VaultRecordQueryParams, additional *model.VaultRecordAdditionalQueryParams) (records []model.VaultRecord, err error) { | ||
|
||
// List all Service Accounts | ||
func (s *ServiceAccountService) List(query *model.ServiceAccountQueryParams, additional *model.ServiceAccountAdditionalQueryParams) (list []model.ServiceAccount, err error) { | ||
list = []model.ServiceAccount{} | ||
|
||
if query == nil { | ||
query = new(model.ServiceAccountQueryParams) | ||
} | ||
if additional != nil { | ||
query.Additional = additional | ||
} | ||
|
||
searchRange := model.NewRange() | ||
|
||
var response *http.Response | ||
|
||
for ok := true; ok; ok = searchRange.NextPage() { | ||
|
||
errorReport := new(model.ErrorReport) | ||
results := new(model.ServiceAccountList) | ||
response, err = s.sling.New().Get("").QueryStruct(query).Add(searchRange.GetRequestRangeHeader()).Add(searchRange.GetRequestModeHeader()).Receive(results, errorReport) | ||
searchRange.ParseResponse(response) | ||
|
||
if errorReport.Code > 0 { | ||
err = errorReport.Wrap("Could not get Groups.") | ||
} | ||
if err == nil { | ||
if len(results.Items) > 0 { | ||
list = append(list, results.Items...) | ||
} | ||
} | ||
|
||
} | ||
|
||
return | ||
} | ||
|
||
// Create Create a serviceaccount | ||
func (s *ServiceAccountService) Create(serviceaccount *model.ServiceAccount) (result *model.ServiceAccount, err error) { | ||
serviceAccounts := new(model.ServiceAccountList) | ||
results := new(model.ServiceAccountList) | ||
errorReport := new(model.ErrorReport) | ||
serviceAccounts.Items = append(serviceAccounts.Items, *serviceaccount) | ||
|
||
_, err = s.sling.New().Post("").BodyJSON(serviceAccounts).Receive(results, errorReport) | ||
|
||
if errorReport.Code > 0 { | ||
fmt.Println("Wrap", errorReport.Message) | ||
//apiErr := model.NewKeyhubApiError(*errorReport, "Could not create ServiceAccount in System %q.", serviceaccount.System.Name) | ||
err = errorReport.Wrap("Could not create ServiceAccount in System %q.", serviceaccount.System.Name) | ||
|
||
return | ||
} | ||
if err == nil { | ||
if len(results.Items) > 0 { | ||
result = &results.Items[0] | ||
} else { | ||
err = fmt.Errorf("Could not create ServiceAccount") | ||
} | ||
} | ||
|
||
return | ||
} | ||
|
||
// Update Update service account | ||
func (s *ServiceAccountService) Update(serviceAccount *model.ServiceAccount) (result *model.ServiceAccount, err error) { | ||
updated := new(model.ServiceAccount) | ||
errorReport := new(model.ErrorReport) | ||
|
||
selfUrl, _ := url.Parse(serviceAccount.Self().Href) | ||
|
||
_, err = s.sling.New().Path(selfUrl.Path).Put("").BodyJSON(serviceAccount).Receive(updated, errorReport) | ||
if errorReport.Code > 0 { | ||
err = errorReport.Wrap("Could not update ServiceAccount %s", serviceAccount.UUID) | ||
return | ||
} | ||
result = updated | ||
return | ||
} | ||
|
||
// Delete Delete a service account by object | ||
func (s *ServiceAccountService) Delete(serviceAccount *model.ServiceAccount) (err error) { | ||
errorReport := new(model.ErrorReport) | ||
|
||
selfUrl, _ := url.Parse(serviceAccount.Self().Href) | ||
|
||
_, err = s.sling.New().Path(selfUrl.Path).Delete("").Receive(nil, errorReport) | ||
if errorReport.Code > 0 { | ||
err = errorReport.Wrap("Could not delete ServiceAccount %q", serviceAccount.UUID) | ||
} | ||
|
||
return | ||
} | ||
|
||
// DeleteByUUID Delete a service account by uuid for a certain group | ||
func (s *ServiceAccountService) DeleteByUUID(uuid uuid.UUID) (err error) { | ||
serviceAccount, err := s.GetByUUID(uuid) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return s.Delete(serviceAccount) | ||
} | ||
|
||
// DeleteByID Delete a service account by ID | ||
func (s *ServiceAccountService) DeleteByID(id int64) (err error) { | ||
serviceAccount, err := s.GetById(id) | ||
if err != nil { | ||
return err | ||
} | ||
return s.Delete(serviceAccount) | ||
} |