mirror of https://github.com/harness/drone.git
ssh public keys: db and api (#2058)
parent
8d5c83d035
commit
0d72a20450
|
@ -34,6 +34,7 @@ type Controller struct {
|
||||||
principalStore store.PrincipalStore
|
principalStore store.PrincipalStore
|
||||||
tokenStore store.TokenStore
|
tokenStore store.TokenStore
|
||||||
membershipStore store.MembershipStore
|
membershipStore store.MembershipStore
|
||||||
|
publicKeyStore store.PublicKeyStore
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewController(
|
func NewController(
|
||||||
|
@ -43,6 +44,7 @@ func NewController(
|
||||||
principalStore store.PrincipalStore,
|
principalStore store.PrincipalStore,
|
||||||
tokenStore store.TokenStore,
|
tokenStore store.TokenStore,
|
||||||
membershipStore store.MembershipStore,
|
membershipStore store.MembershipStore,
|
||||||
|
publicKeyStore store.PublicKeyStore,
|
||||||
) *Controller {
|
) *Controller {
|
||||||
return &Controller{
|
return &Controller{
|
||||||
tx: tx,
|
tx: tx,
|
||||||
|
@ -51,6 +53,7 @@ func NewController(
|
||||||
principalStore: principalStore,
|
principalStore: principalStore,
|
||||||
tokenStore: tokenStore,
|
tokenStore: tokenStore,
|
||||||
membershipStore: membershipStore,
|
membershipStore: membershipStore,
|
||||||
|
publicKeyStore: publicKeyStore,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,119 @@
|
||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed 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 user
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
apiauth "github.com/harness/gitness/app/api/auth"
|
||||||
|
"github.com/harness/gitness/app/auth"
|
||||||
|
"github.com/harness/gitness/app/services/publickey"
|
||||||
|
"github.com/harness/gitness/errors"
|
||||||
|
"github.com/harness/gitness/types"
|
||||||
|
"github.com/harness/gitness/types/check"
|
||||||
|
"github.com/harness/gitness/types/enum"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CreatePublicKeyInput struct {
|
||||||
|
Identifier string `json:"identifier"`
|
||||||
|
Usage enum.PublicKeyUsage `json:"usage"`
|
||||||
|
Content string `json:"content"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) CreatePublicKey(
|
||||||
|
ctx context.Context,
|
||||||
|
session *auth.Session,
|
||||||
|
userUID string,
|
||||||
|
in *CreatePublicKeyInput,
|
||||||
|
) (*types.PublicKey, error) {
|
||||||
|
user, err := c.principalStore.FindUserByUID(ctx, userUID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to fetch user by uid: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = apiauth.CheckUser(ctx, c.authorizer, session, user, enum.PermissionUserEdit); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := sanitizeCreatePublicKeyInput(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
key, comment, err := publickey.ParseString(in.Content)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.InvalidArgument("could not parse public key")
|
||||||
|
}
|
||||||
|
|
||||||
|
now := time.Now().UnixMilli()
|
||||||
|
|
||||||
|
k := &types.PublicKey{
|
||||||
|
PrincipalID: user.ID,
|
||||||
|
Created: now,
|
||||||
|
Verified: nil, // the key is created as unverified
|
||||||
|
Identifier: in.Identifier,
|
||||||
|
Usage: in.Usage,
|
||||||
|
Fingerprint: key.Fingerprint(),
|
||||||
|
Content: in.Content,
|
||||||
|
Comment: comment,
|
||||||
|
Type: key.Type(),
|
||||||
|
}
|
||||||
|
|
||||||
|
err = c.tx.WithTx(ctx, func(ctx context.Context) error {
|
||||||
|
existingKeys, err := c.publicKeyStore.ListByFingerprint(ctx, k.Fingerprint)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to read keys by fingerprint: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, existingKey := range existingKeys {
|
||||||
|
if key.Matches(existingKey.Content) {
|
||||||
|
return errors.InvalidArgument("Key is already in use")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = c.publicKeyStore.Create(ctx, k)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to insert public key: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return k, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func sanitizeCreatePublicKeyInput(in *CreatePublicKeyInput) error {
|
||||||
|
if err := check.Identifier(in.Identifier); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
usage, ok := in.Usage.Sanitize()
|
||||||
|
if !ok {
|
||||||
|
return errors.InvalidArgument("invalid value for public key usage")
|
||||||
|
}
|
||||||
|
in.Usage = usage
|
||||||
|
|
||||||
|
in.Content = strings.TrimSpace(in.Content)
|
||||||
|
if in.Content == "" {
|
||||||
|
return errors.InvalidArgument("public key not provided")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed 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 user
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
apiauth "github.com/harness/gitness/app/api/auth"
|
||||||
|
"github.com/harness/gitness/app/auth"
|
||||||
|
"github.com/harness/gitness/types/enum"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *Controller) DeletePublicKey(
|
||||||
|
ctx context.Context,
|
||||||
|
session *auth.Session,
|
||||||
|
userUID string,
|
||||||
|
identifier string,
|
||||||
|
) error {
|
||||||
|
user, err := c.principalStore.FindUserByUID(ctx, userUID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to fetch user by uid: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = apiauth.CheckUser(ctx, c.authorizer, session, user, enum.PermissionUserEdit); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = c.publicKeyStore.DeleteByIdentifier(ctx, user.ID, identifier)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to delete public key by id: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed 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 user
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
apiauth "github.com/harness/gitness/app/api/auth"
|
||||||
|
"github.com/harness/gitness/app/auth"
|
||||||
|
"github.com/harness/gitness/store/database/dbtx"
|
||||||
|
"github.com/harness/gitness/types"
|
||||||
|
"github.com/harness/gitness/types/enum"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *Controller) ListPublicKeys(
|
||||||
|
ctx context.Context,
|
||||||
|
session *auth.Session,
|
||||||
|
userUID string,
|
||||||
|
filter *types.PublicKeyFilter,
|
||||||
|
) ([]types.PublicKey, int, error) {
|
||||||
|
user, err := c.principalStore.FindUserByUID(ctx, userUID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, fmt.Errorf("failed to fetch user by uid: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = apiauth.CheckUser(ctx, c.authorizer, session, user, enum.PermissionUserView); err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
list []types.PublicKey
|
||||||
|
count int
|
||||||
|
)
|
||||||
|
|
||||||
|
err = c.tx.WithTx(ctx, func(ctx context.Context) error {
|
||||||
|
list, err = c.publicKeyStore.List(ctx, user.ID, filter)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to list public keys for user: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if filter.Page == 1 && len(list) < filter.Size {
|
||||||
|
count = len(list)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
count, err = c.publicKeyStore.Count(ctx, user.ID, filter)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to count public keys for user: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}, dbtx.TxDefaultReadOnly)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return list, count, nil
|
||||||
|
}
|
|
@ -35,6 +35,7 @@ func ProvideController(
|
||||||
principalStore store.PrincipalStore,
|
principalStore store.PrincipalStore,
|
||||||
tokenStore store.TokenStore,
|
tokenStore store.TokenStore,
|
||||||
membershipStore store.MembershipStore,
|
membershipStore store.MembershipStore,
|
||||||
|
publicKeyStore store.PublicKeyStore,
|
||||||
) *Controller {
|
) *Controller {
|
||||||
return NewController(
|
return NewController(
|
||||||
tx,
|
tx,
|
||||||
|
@ -42,5 +43,6 @@ func ProvideController(
|
||||||
authorizer,
|
authorizer,
|
||||||
principalStore,
|
principalStore,
|
||||||
tokenStore,
|
tokenStore,
|
||||||
membershipStore)
|
membershipStore,
|
||||||
|
publicKeyStore)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed 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 user
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/harness/gitness/app/api/controller/user"
|
||||||
|
"github.com/harness/gitness/app/api/render"
|
||||||
|
"github.com/harness/gitness/app/api/request"
|
||||||
|
)
|
||||||
|
|
||||||
|
func HandleCreatePublicKey(userCtrl *user.Controller) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
session, _ := request.AuthSessionFrom(ctx)
|
||||||
|
userUID := session.Principal.UID
|
||||||
|
|
||||||
|
in := new(user.CreatePublicKeyInput)
|
||||||
|
err := json.NewDecoder(r.Body).Decode(in)
|
||||||
|
if err != nil {
|
||||||
|
render.BadRequestf(ctx, w, "Invalid Request Body: %s.", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
key, err := userCtrl.CreatePublicKey(ctx, session, userUID, in)
|
||||||
|
if err != nil {
|
||||||
|
render.TranslatedUserError(ctx, w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
render.JSON(w, http.StatusCreated, key)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed 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 user
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/harness/gitness/app/api/controller/user"
|
||||||
|
"github.com/harness/gitness/app/api/render"
|
||||||
|
"github.com/harness/gitness/app/api/request"
|
||||||
|
)
|
||||||
|
|
||||||
|
func HandleDeletePublicKey(userCtrl *user.Controller) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
session, _ := request.AuthSessionFrom(ctx)
|
||||||
|
userUID := session.Principal.UID
|
||||||
|
|
||||||
|
id, err := request.GetPublicKeyIdentifierFromPath(r)
|
||||||
|
if err != nil {
|
||||||
|
render.BadRequest(ctx, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = userCtrl.DeletePublicKey(ctx, session, userUID, id)
|
||||||
|
if err != nil {
|
||||||
|
render.TranslatedUserError(ctx, w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
render.DeleteSuccessful(w)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed 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 user
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/harness/gitness/app/api/controller/user"
|
||||||
|
"github.com/harness/gitness/app/api/render"
|
||||||
|
"github.com/harness/gitness/app/api/request"
|
||||||
|
)
|
||||||
|
|
||||||
|
func HandleListPublicKeys(userCtrl *user.Controller) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
session, _ := request.AuthSessionFrom(ctx)
|
||||||
|
userUID := session.Principal.UID
|
||||||
|
|
||||||
|
filter, err := request.ParseListPublicKeyQueryFilterFromRequest(r)
|
||||||
|
if err != nil {
|
||||||
|
render.TranslatedUserError(ctx, w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
keys, count, err := userCtrl.ListPublicKeys(ctx, session, userUID, &filter)
|
||||||
|
if err != nil {
|
||||||
|
render.TranslatedUserError(ctx, w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
render.Pagination(r, w, filter.Page, filter.Size, count)
|
||||||
|
render.JSON(w, http.StatusOK, keys)
|
||||||
|
}
|
||||||
|
}
|
|
@ -61,6 +61,36 @@ var queryParameterSortMembershipSpaces = openapi3.ParameterOrRef{
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var queryParameterQueryPublicKey = openapi3.ParameterOrRef{
|
||||||
|
Parameter: &openapi3.Parameter{
|
||||||
|
Name: request.QueryParamQuery,
|
||||||
|
In: openapi3.ParameterInQuery,
|
||||||
|
Description: ptr.String("The substring which is used to filter the public keys by their path identifier."),
|
||||||
|
Required: ptr.Bool(false),
|
||||||
|
Schema: &openapi3.SchemaOrRef{
|
||||||
|
Schema: &openapi3.Schema{
|
||||||
|
Type: ptrSchemaType(openapi3.SchemaTypeString),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var queryParameterSortPublicKey = openapi3.ParameterOrRef{
|
||||||
|
Parameter: &openapi3.Parameter{
|
||||||
|
Name: request.QueryParamSort,
|
||||||
|
In: openapi3.ParameterInQuery,
|
||||||
|
Description: ptr.String("The data by which the public keys are sorted."),
|
||||||
|
Required: ptr.Bool(false),
|
||||||
|
Schema: &openapi3.SchemaOrRef{
|
||||||
|
Schema: &openapi3.Schema{
|
||||||
|
Type: ptrSchemaType(openapi3.SchemaTypeString),
|
||||||
|
Default: ptrptr(enum.PublicKeySortCreated),
|
||||||
|
Enum: enum.PublicKeySort("").Enum(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
// helper function that constructs the openapi specification
|
// helper function that constructs the openapi specification
|
||||||
// for user account resources.
|
// for user account resources.
|
||||||
func buildUser(reflector *openapi3.Reflector) {
|
func buildUser(reflector *openapi3.Reflector) {
|
||||||
|
@ -99,4 +129,35 @@ func buildUser(reflector *openapi3.Reflector) {
|
||||||
_ = reflector.SetJSONResponse(&opMemberSpaces, new([]types.MembershipSpace), http.StatusOK)
|
_ = reflector.SetJSONResponse(&opMemberSpaces, new([]types.MembershipSpace), http.StatusOK)
|
||||||
_ = reflector.SetJSONResponse(&opMemberSpaces, new(usererror.Error), http.StatusInternalServerError)
|
_ = reflector.SetJSONResponse(&opMemberSpaces, new(usererror.Error), http.StatusInternalServerError)
|
||||||
_ = reflector.Spec.AddOperation(http.MethodGet, "/user/memberships", opMemberSpaces)
|
_ = reflector.Spec.AddOperation(http.MethodGet, "/user/memberships", opMemberSpaces)
|
||||||
|
|
||||||
|
opKeyCreate := openapi3.Operation{}
|
||||||
|
opKeyCreate.WithTags("user")
|
||||||
|
opKeyCreate.WithMapOfAnything(map[string]interface{}{"operationId": "createPublicKey"})
|
||||||
|
_ = reflector.SetRequest(&opKeyCreate, new(user.CreatePublicKeyInput), http.MethodPost)
|
||||||
|
_ = reflector.SetJSONResponse(&opKeyCreate, new(types.PublicKey), http.StatusCreated)
|
||||||
|
_ = reflector.SetJSONResponse(&opKeyCreate, new(usererror.Error), http.StatusBadRequest)
|
||||||
|
_ = reflector.SetJSONResponse(&opKeyCreate, new(usererror.Error), http.StatusInternalServerError)
|
||||||
|
_ = reflector.Spec.AddOperation(http.MethodPost, "/user/keys", opKeyCreate)
|
||||||
|
|
||||||
|
opKeyDelete := openapi3.Operation{}
|
||||||
|
opKeyDelete.WithTags("user")
|
||||||
|
opKeyDelete.WithMapOfAnything(map[string]interface{}{"operationId": "deletePublicKey"})
|
||||||
|
_ = reflector.SetRequest(&opKeyDelete, struct {
|
||||||
|
ID string `path:"public_key_identifier"`
|
||||||
|
}{}, http.MethodDelete)
|
||||||
|
_ = reflector.SetJSONResponse(&opKeyDelete, nil, http.StatusNoContent)
|
||||||
|
_ = reflector.SetJSONResponse(&opKeyDelete, new(usererror.Error), http.StatusNotFound)
|
||||||
|
_ = reflector.SetJSONResponse(&opKeyDelete, new(usererror.Error), http.StatusInternalServerError)
|
||||||
|
_ = reflector.Spec.AddOperation(http.MethodDelete, "/user/keys/{public_key_identifier}", opKeyDelete)
|
||||||
|
|
||||||
|
opKeyList := openapi3.Operation{}
|
||||||
|
opKeyList.WithTags("user")
|
||||||
|
opKeyList.WithMapOfAnything(map[string]interface{}{"operationId": "listPublicKey"})
|
||||||
|
opKeyList.WithParameters(queryParameterPage, queryParameterLimit,
|
||||||
|
queryParameterQueryPublicKey, queryParameterSortPublicKey, queryParameterOrder)
|
||||||
|
_ = reflector.SetRequest(&opKeyList, struct{}{}, http.MethodGet)
|
||||||
|
_ = reflector.SetJSONResponse(&opKeyList, new([]types.PublicKey), http.StatusOK)
|
||||||
|
_ = reflector.SetJSONResponse(&opKeyList, new(usererror.Error), http.StatusBadRequest)
|
||||||
|
_ = reflector.SetJSONResponse(&opKeyList, new(usererror.Error), http.StatusInternalServerError)
|
||||||
|
_ = reflector.Spec.AddOperation(http.MethodGet, "/user/keys", opKeyList)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed 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 request
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"github.com/harness/gitness/app/api/usererror"
|
||||||
|
"github.com/harness/gitness/types"
|
||||||
|
"github.com/harness/gitness/types/enum"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
PathParamPublicKeyIdentifier = "public_key_identifier"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetPublicKeyIdentifierFromPath(r *http.Request) (string, error) {
|
||||||
|
identifier, err := PathParamOrError(r, PathParamPublicKeyIdentifier)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// paths are unescaped
|
||||||
|
return url.PathUnescape(identifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseListPublicKeyQueryFilterFromRequest parses query filter for public keys from the url.
|
||||||
|
func ParseListPublicKeyQueryFilterFromRequest(r *http.Request) (types.PublicKeyFilter, error) {
|
||||||
|
sort := enum.PublicKeySort(ParseSort(r))
|
||||||
|
sort, ok := sort.Sanitize()
|
||||||
|
if !ok {
|
||||||
|
return types.PublicKeyFilter{}, usererror.BadRequest("Invalid value for the sort query parameter.")
|
||||||
|
}
|
||||||
|
|
||||||
|
return types.PublicKeyFilter{
|
||||||
|
ListQueryFilter: ParseListQueryFilterFromRequest(r),
|
||||||
|
Sort: sort,
|
||||||
|
Order: ParseOrder(r),
|
||||||
|
}, nil
|
||||||
|
}
|
|
@ -628,6 +628,14 @@ func setupUser(r chi.Router, userCtrl *user.Controller) {
|
||||||
r.Delete("/", handleruser.HandleDeleteToken(userCtrl, enum.TokenTypeSession))
|
r.Delete("/", handleruser.HandleDeleteToken(userCtrl, enum.TokenTypeSession))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Private keys
|
||||||
|
r.Route("/keys", func(r chi.Router) {
|
||||||
|
r.Get("/", handleruser.HandleListPublicKeys(userCtrl))
|
||||||
|
r.Post("/", handleruser.HandleCreatePublicKey(userCtrl))
|
||||||
|
r.Delete(fmt.Sprintf("/{%s}", request.PathParamPublicKeyIdentifier),
|
||||||
|
handleruser.HandleDeletePublicKey(userCtrl))
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,100 @@
|
||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed 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 publickey
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/base64"
|
||||||
|
|
||||||
|
"github.com/harness/gitness/errors"
|
||||||
|
|
||||||
|
"github.com/gliderlabs/ssh"
|
||||||
|
gossh "golang.org/x/crypto/ssh"
|
||||||
|
"golang.org/x/exp/slices"
|
||||||
|
)
|
||||||
|
|
||||||
|
var AllowedTypes = []string{
|
||||||
|
gossh.KeyAlgoRSA,
|
||||||
|
gossh.KeyAlgoECDSA256,
|
||||||
|
gossh.KeyAlgoECDSA384,
|
||||||
|
gossh.KeyAlgoECDSA521,
|
||||||
|
gossh.KeyAlgoED25519,
|
||||||
|
gossh.KeyAlgoSKECDSA256,
|
||||||
|
gossh.KeyAlgoSKED25519,
|
||||||
|
}
|
||||||
|
|
||||||
|
var DisallowedTypes = []string{
|
||||||
|
gossh.KeyAlgoDSA,
|
||||||
|
}
|
||||||
|
|
||||||
|
func From(key gossh.PublicKey) KeyInfo {
|
||||||
|
return KeyInfo{
|
||||||
|
Key: key,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseString(keyData string) (KeyInfo, string, error) {
|
||||||
|
return Parse([]byte(keyData))
|
||||||
|
}
|
||||||
|
|
||||||
|
func Parse(keyData []byte) (KeyInfo, string, error) {
|
||||||
|
publicKey, comment, _, _, err := gossh.ParseAuthorizedKey(keyData)
|
||||||
|
if err != nil {
|
||||||
|
return KeyInfo{}, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
keyType := publicKey.Type()
|
||||||
|
|
||||||
|
// explicitly disallowed
|
||||||
|
if slices.Contains(DisallowedTypes, keyType) {
|
||||||
|
return KeyInfo{}, "", errors.InvalidArgument("keys of type %s are not allowed", keyType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// only allowed
|
||||||
|
if !slices.Contains(AllowedTypes, keyType) {
|
||||||
|
return KeyInfo{}, "", errors.InvalidArgument("allowed key types are %v", AllowedTypes)
|
||||||
|
}
|
||||||
|
|
||||||
|
return KeyInfo{
|
||||||
|
Key: publicKey,
|
||||||
|
}, comment, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type KeyInfo struct {
|
||||||
|
Key gossh.PublicKey
|
||||||
|
}
|
||||||
|
|
||||||
|
func (key KeyInfo) Matches(s string) bool {
|
||||||
|
otherKey, _, _, _, err := gossh.ParseAuthorizedKey([]byte(s))
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return key.MatchesKey(otherKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (key KeyInfo) MatchesKey(otherKey gossh.PublicKey) bool {
|
||||||
|
return ssh.KeysEqual(key.Key, otherKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (key KeyInfo) Fingerprint() string {
|
||||||
|
sum := sha256.New()
|
||||||
|
sum.Write(key.Key.Marshal())
|
||||||
|
return base64.StdEncoding.EncodeToString(sum.Sum(nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (key KeyInfo) Type() string {
|
||||||
|
return key.Key.Type()
|
||||||
|
}
|
|
@ -0,0 +1,91 @@
|
||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed 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 publickey
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/harness/gitness/app/store"
|
||||||
|
"github.com/harness/gitness/errors"
|
||||||
|
"github.com/harness/gitness/types"
|
||||||
|
"github.com/harness/gitness/types/enum"
|
||||||
|
|
||||||
|
"github.com/gliderlabs/ssh"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Service interface {
|
||||||
|
ValidateKey(ctx context.Context, publicKey ssh.PublicKey, usage enum.PublicKeyUsage) (*types.PrincipalInfo, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewService(
|
||||||
|
publicKeyStore store.PublicKeyStore,
|
||||||
|
pCache store.PrincipalInfoCache,
|
||||||
|
) LocalService {
|
||||||
|
return LocalService{
|
||||||
|
publicKeyStore: publicKeyStore,
|
||||||
|
pCache: pCache,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type LocalService struct {
|
||||||
|
publicKeyStore store.PublicKeyStore
|
||||||
|
pCache store.PrincipalInfoCache
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateKey tries to match the provided key to one of the keys in the database.
|
||||||
|
// It updates the verified timestamp of the matched key to mark it as used.
|
||||||
|
func (s LocalService) ValidateKey(
|
||||||
|
ctx context.Context,
|
||||||
|
publicKey ssh.PublicKey,
|
||||||
|
usage enum.PublicKeyUsage,
|
||||||
|
) (*types.PrincipalInfo, error) {
|
||||||
|
key := From(publicKey)
|
||||||
|
fingerprint := key.Fingerprint()
|
||||||
|
|
||||||
|
existingKeys, err := s.publicKeyStore.ListByFingerprint(ctx, fingerprint)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to read keys by fingerprint: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var keyID int64
|
||||||
|
var principalID int64
|
||||||
|
|
||||||
|
for _, existingKey := range existingKeys {
|
||||||
|
if !key.Matches(existingKey.Content) || existingKey.Usage != usage {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
keyID = existingKey.ID
|
||||||
|
principalID = existingKey.PrincipalID
|
||||||
|
}
|
||||||
|
|
||||||
|
if keyID == 0 {
|
||||||
|
return nil, errors.NotFound("Unrecognized key")
|
||||||
|
}
|
||||||
|
|
||||||
|
pInfo, err := s.pCache.Get(ctx, principalID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to pull principal info by public key's principal ID: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = s.publicKeyStore.MarkAsVerified(ctx, keyID, time.Now().UnixMilli())
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed mark key as verified: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return pInfo, nil
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed 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 publickey
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/harness/gitness/app/store"
|
||||||
|
|
||||||
|
"github.com/google/wire"
|
||||||
|
)
|
||||||
|
|
||||||
|
var WireSet = wire.NewSet(
|
||||||
|
ProvidePublicKey,
|
||||||
|
)
|
||||||
|
|
||||||
|
func ProvidePublicKey(
|
||||||
|
publicKeyStore store.PublicKeyStore,
|
||||||
|
pCache store.PrincipalInfoCache,
|
||||||
|
) Service {
|
||||||
|
return NewService(publicKeyStore, pCache)
|
||||||
|
}
|
|
@ -806,4 +806,30 @@ type (
|
||||||
// FindByIdentifier returns a types.UserGroup given a space ID and identifier.
|
// FindByIdentifier returns a types.UserGroup given a space ID and identifier.
|
||||||
FindByIdentifier(ctx context.Context, spaceID int64, identifier string) (*types.UserGroup, error)
|
FindByIdentifier(ctx context.Context, spaceID int64, identifier string) (*types.UserGroup, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PublicKeyStore interface {
|
||||||
|
// Find returns a public key given an ID.
|
||||||
|
Find(ctx context.Context, id int64) (*types.PublicKey, error)
|
||||||
|
|
||||||
|
// FindByIdentifier returns a public key given a principal ID and an identifier.
|
||||||
|
FindByIdentifier(ctx context.Context, principalID int64, identifier string) (*types.PublicKey, error)
|
||||||
|
|
||||||
|
// Create creates a new public key.
|
||||||
|
Create(ctx context.Context, publicKey *types.PublicKey) error
|
||||||
|
|
||||||
|
// DeleteByIdentifier deletes a public key.
|
||||||
|
DeleteByIdentifier(ctx context.Context, principalID int64, identifier string) error
|
||||||
|
|
||||||
|
// MarkAsVerified updates the public key to mark it as verified.
|
||||||
|
MarkAsVerified(ctx context.Context, id int64, verified int64) error
|
||||||
|
|
||||||
|
// Count returns the number of public keys for the principal that match provided the filter.
|
||||||
|
Count(ctx context.Context, principalID int64, filter *types.PublicKeyFilter) (int, error)
|
||||||
|
|
||||||
|
// List returns the public keys for the principal that match provided the filter.
|
||||||
|
List(ctx context.Context, principalID int64, filter *types.PublicKeyFilter) ([]types.PublicKey, error)
|
||||||
|
|
||||||
|
// ListByFingerprint returns public keys given a fingerprint and key usage.
|
||||||
|
ListByFingerprint(ctx context.Context, fingerprint string) ([]types.PublicKey, error)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
DROP INDEX public_keys_usage_fingerprint;
|
||||||
|
DROP INDEX public_keys_principal_id;
|
||||||
|
DROP TABLE public_keys;
|
|
@ -0,0 +1,22 @@
|
||||||
|
CREATE TABLE public_keys (
|
||||||
|
public_key_id SERIAL PRIMARY KEY
|
||||||
|
,public_key_principal_id INTEGER NOT NULL
|
||||||
|
,public_key_created BIGINT NOT NULL
|
||||||
|
,public_key_verified BIGINT
|
||||||
|
,public_key_identifier TEXT NOT NULL
|
||||||
|
,public_key_usage TEXT NOT NULL
|
||||||
|
,public_key_fingerprint TEXT NOT NULL
|
||||||
|
,public_key_content TEXT NOT NULL
|
||||||
|
,public_key_comment TEXT NOT NULL
|
||||||
|
,public_key_type TEXT NOT NULL
|
||||||
|
,CONSTRAINT fk_public_key_principal_id FOREIGN KEY (public_key_principal_id)
|
||||||
|
REFERENCES principals (principal_id) MATCH SIMPLE
|
||||||
|
ON UPDATE NO ACTION
|
||||||
|
ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX public_keys_fingerprint
|
||||||
|
ON public_keys(public_key_fingerprint);
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX public_keys_principal_id_identifier
|
||||||
|
ON public_keys(public_key_principal_id, LOWER(public_key_identifier));
|
|
@ -0,0 +1,3 @@
|
||||||
|
DROP INDEX public_keys_usage_fingerprint;
|
||||||
|
DROP INDEX public_keys_principal_id;
|
||||||
|
DROP TABLE public_keys;
|
|
@ -0,0 +1,22 @@
|
||||||
|
CREATE TABLE public_keys (
|
||||||
|
public_key_id INTEGER PRIMARY KEY AUTOINCREMENT
|
||||||
|
,public_key_principal_id INTEGER NOT NULL
|
||||||
|
,public_key_created BIGINT NOT NULL
|
||||||
|
,public_key_verified BIGINT
|
||||||
|
,public_key_identifier TEXT NOT NULL
|
||||||
|
,public_key_usage TEXT NOT NULL
|
||||||
|
,public_key_fingerprint TEXT NOT NULL
|
||||||
|
,public_key_content TEXT NOT NULL
|
||||||
|
,public_key_comment TEXT NOT NULL
|
||||||
|
,public_key_type TEXT NOT NULL
|
||||||
|
,CONSTRAINT fk_public_key_principal_id FOREIGN KEY (public_key_principal_id)
|
||||||
|
REFERENCES principals (principal_id) MATCH SIMPLE
|
||||||
|
ON UPDATE NO ACTION
|
||||||
|
ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX public_keys_fingerprint
|
||||||
|
ON public_keys(public_key_fingerprint);
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX public_keys_principal_id_identifier
|
||||||
|
ON public_keys(public_key_principal_id, LOWER(public_key_identifier));
|
|
@ -0,0 +1,356 @@
|
||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed 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 database
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/harness/gitness/app/store"
|
||||||
|
"github.com/harness/gitness/errors"
|
||||||
|
"github.com/harness/gitness/store/database"
|
||||||
|
"github.com/harness/gitness/store/database/dbtx"
|
||||||
|
"github.com/harness/gitness/types"
|
||||||
|
"github.com/harness/gitness/types/enum"
|
||||||
|
|
||||||
|
"github.com/Masterminds/squirrel"
|
||||||
|
"github.com/guregu/null"
|
||||||
|
"github.com/jmoiron/sqlx"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ store.PublicKeyStore = PublicKeyStore{}
|
||||||
|
|
||||||
|
// NewPublicKeyStore returns a new PublicKeyStore.
|
||||||
|
func NewPublicKeyStore(db *sqlx.DB) PublicKeyStore {
|
||||||
|
return PublicKeyStore{
|
||||||
|
db: db,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PublicKeyStore implements a store.PublicKeyStore backed by a relational database.
|
||||||
|
type PublicKeyStore struct {
|
||||||
|
db *sqlx.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
type publicKey struct {
|
||||||
|
ID int64 `db:"public_key_id"`
|
||||||
|
|
||||||
|
PrincipalID int64 `db:"public_key_principal_id"`
|
||||||
|
|
||||||
|
Created int64 `db:"public_key_created"`
|
||||||
|
Verified null.Int `db:"public_key_verified"`
|
||||||
|
|
||||||
|
Identifier string `db:"public_key_identifier"`
|
||||||
|
Usage string `db:"public_key_usage"`
|
||||||
|
|
||||||
|
Fingerprint string `db:"public_key_fingerprint"`
|
||||||
|
Content string `db:"public_key_content"`
|
||||||
|
Comment string `db:"public_key_comment"`
|
||||||
|
Type string `db:"public_key_type"`
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
publicKeyColumns = `
|
||||||
|
public_key_id
|
||||||
|
,public_key_principal_id
|
||||||
|
,public_key_created
|
||||||
|
,public_key_verified
|
||||||
|
,public_key_identifier
|
||||||
|
,public_key_usage
|
||||||
|
,public_key_fingerprint
|
||||||
|
,public_key_content
|
||||||
|
,public_key_comment
|
||||||
|
,public_key_type`
|
||||||
|
|
||||||
|
publicKeySelectBase = `
|
||||||
|
SELECT` + publicKeyColumns + `
|
||||||
|
FROM public_keys`
|
||||||
|
)
|
||||||
|
|
||||||
|
// Find fetches a job by its unique identifier.
|
||||||
|
func (s PublicKeyStore) Find(ctx context.Context, id int64) (*types.PublicKey, error) {
|
||||||
|
const sqlQuery = publicKeySelectBase + `
|
||||||
|
WHERE public_key_id = $1`
|
||||||
|
|
||||||
|
db := dbtx.GetAccessor(ctx, s.db)
|
||||||
|
|
||||||
|
result := &publicKey{}
|
||||||
|
if err := db.GetContext(ctx, result, sqlQuery, id); err != nil {
|
||||||
|
return nil, database.ProcessSQLErrorf(ctx, err, "Failed to find public key by id")
|
||||||
|
}
|
||||||
|
|
||||||
|
key := mapToPublicKey(result)
|
||||||
|
|
||||||
|
return &key, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindByIdentifier returns a public key given a principal ID and an identifier.
|
||||||
|
func (s PublicKeyStore) FindByIdentifier(
|
||||||
|
ctx context.Context,
|
||||||
|
principalID int64,
|
||||||
|
identifier string,
|
||||||
|
) (*types.PublicKey, error) {
|
||||||
|
const sqlQuery = publicKeySelectBase + `
|
||||||
|
WHERE public_key_principal_id = $1 and LOWER(public_key_identifier) = $2`
|
||||||
|
|
||||||
|
db := dbtx.GetAccessor(ctx, s.db)
|
||||||
|
|
||||||
|
result := &publicKey{}
|
||||||
|
if err := db.GetContext(ctx, result, sqlQuery, principalID, strings.ToLower(identifier)); err != nil {
|
||||||
|
return nil, database.ProcessSQLErrorf(ctx, err, "Failed to find public key by principal and identifier")
|
||||||
|
}
|
||||||
|
|
||||||
|
key := mapToPublicKey(result)
|
||||||
|
|
||||||
|
return &key, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create creates a new public key.
|
||||||
|
func (s PublicKeyStore) Create(ctx context.Context, key *types.PublicKey) error {
|
||||||
|
const sqlQuery = `
|
||||||
|
INSERT INTO public_keys (
|
||||||
|
public_key_principal_id
|
||||||
|
,public_key_created
|
||||||
|
,public_key_verified
|
||||||
|
,public_key_identifier
|
||||||
|
,public_key_usage
|
||||||
|
,public_key_fingerprint
|
||||||
|
,public_key_content
|
||||||
|
,public_key_comment
|
||||||
|
,public_key_type
|
||||||
|
) values (
|
||||||
|
:public_key_principal_id
|
||||||
|
,:public_key_created
|
||||||
|
,:public_key_verified
|
||||||
|
,:public_key_identifier
|
||||||
|
,:public_key_usage
|
||||||
|
,:public_key_fingerprint
|
||||||
|
,:public_key_content
|
||||||
|
,:public_key_comment
|
||||||
|
,:public_key_type
|
||||||
|
) RETURNING public_key_id`
|
||||||
|
|
||||||
|
db := dbtx.GetAccessor(ctx, s.db)
|
||||||
|
|
||||||
|
dbKey := mapToInternalPublicKey(key)
|
||||||
|
|
||||||
|
query, arg, err := db.BindNamed(sqlQuery, &dbKey)
|
||||||
|
if err != nil {
|
||||||
|
return database.ProcessSQLErrorf(ctx, err, "Failed to bind public key object")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = db.QueryRowContext(ctx, query, arg...).Scan(&dbKey.ID); err != nil {
|
||||||
|
return database.ProcessSQLErrorf(ctx, err, "Insert public key query failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
key.ID = dbKey.ID
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteByIdentifier deletes a public key.
|
||||||
|
func (s PublicKeyStore) DeleteByIdentifier(ctx context.Context, principalID int64, identifier string) error {
|
||||||
|
const sqlQuery = `DELETE FROM public_keys WHERE public_key_principal_id = $1 and LOWER(public_key_identifier) = $2`
|
||||||
|
|
||||||
|
db := dbtx.GetAccessor(ctx, s.db)
|
||||||
|
|
||||||
|
result, err := db.ExecContext(ctx, sqlQuery, principalID, strings.ToLower(identifier))
|
||||||
|
if err != nil {
|
||||||
|
return database.ProcessSQLErrorf(ctx, err, "Delete public key query failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
count, err := result.RowsAffected()
|
||||||
|
if err != nil {
|
||||||
|
return database.ProcessSQLErrorf(ctx, err, "RowsAffected after delete of public key failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
if count == 0 {
|
||||||
|
return errors.NotFound("Key not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarkAsVerified updates the public key to mark it as verified.
|
||||||
|
func (s PublicKeyStore) MarkAsVerified(ctx context.Context, id int64, verified int64) error {
|
||||||
|
const sqlQuery = `
|
||||||
|
UPDATE public_keys
|
||||||
|
SET public_key_verified = $1
|
||||||
|
WHERE public_key_id = $2`
|
||||||
|
|
||||||
|
if _, err := dbtx.GetAccessor(ctx, s.db).ExecContext(ctx, sqlQuery, verified, id); err != nil {
|
||||||
|
return database.ProcessSQLErrorf(ctx, err, "Failed to mark public key as varified")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s PublicKeyStore) Count(
|
||||||
|
ctx context.Context,
|
||||||
|
principalID int64,
|
||||||
|
filter *types.PublicKeyFilter,
|
||||||
|
) (int, error) {
|
||||||
|
stmt := database.Builder.
|
||||||
|
Select("count(*)").
|
||||||
|
From("public_keys").
|
||||||
|
Where("public_key_principal_id = ?", principalID)
|
||||||
|
|
||||||
|
stmt = s.applyQueryFilter(stmt, filter)
|
||||||
|
|
||||||
|
sql, args, err := stmt.ToSql()
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("failed to convert query to sql: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
db := dbtx.GetAccessor(ctx, s.db)
|
||||||
|
|
||||||
|
var count int
|
||||||
|
|
||||||
|
if err := db.QueryRowContext(ctx, sql, args...).Scan(&count); err != nil {
|
||||||
|
return 0, database.ProcessSQLErrorf(ctx, err, "failed to execute count public keys query")
|
||||||
|
}
|
||||||
|
|
||||||
|
return count, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// List returns the public keys for the principal.
|
||||||
|
func (s PublicKeyStore) List(
|
||||||
|
ctx context.Context,
|
||||||
|
principalID int64,
|
||||||
|
filter *types.PublicKeyFilter,
|
||||||
|
) ([]types.PublicKey, error) {
|
||||||
|
stmt := database.Builder.
|
||||||
|
Select(publicKeyColumns).
|
||||||
|
From("public_keys").
|
||||||
|
Where("public_key_principal_id = ?", principalID)
|
||||||
|
|
||||||
|
stmt = s.applyQueryFilter(stmt, filter)
|
||||||
|
stmt = s.applySortFilter(stmt, filter)
|
||||||
|
|
||||||
|
sql, args, err := stmt.ToSql()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to convert query to sql: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
db := dbtx.GetAccessor(ctx, s.db)
|
||||||
|
|
||||||
|
keys := make([]publicKey, 0)
|
||||||
|
if err = db.SelectContext(ctx, &keys, sql, args...); err != nil {
|
||||||
|
return nil, database.ProcessSQLErrorf(ctx, err, "failed to execute list public keys query")
|
||||||
|
}
|
||||||
|
|
||||||
|
return mapToPublicKeys(keys), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListByFingerprint returns public keys given a fingerprint and key usage.
|
||||||
|
func (s PublicKeyStore) ListByFingerprint(
|
||||||
|
ctx context.Context,
|
||||||
|
fingerprint string,
|
||||||
|
) ([]types.PublicKey, error) {
|
||||||
|
stmt := database.Builder.
|
||||||
|
Select(publicKeyColumns).
|
||||||
|
From("public_keys").
|
||||||
|
Where("public_key_fingerprint = ?", fingerprint).
|
||||||
|
OrderBy("public_key_created ASC")
|
||||||
|
|
||||||
|
sql, args, err := stmt.ToSql()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to convert query to sql: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
db := dbtx.GetAccessor(ctx, s.db)
|
||||||
|
|
||||||
|
keys := make([]publicKey, 0)
|
||||||
|
if err = db.SelectContext(ctx, &keys, sql, args...); err != nil {
|
||||||
|
return nil, database.ProcessSQLErrorf(ctx, err, "failed to execute public keys by fingerprint query")
|
||||||
|
}
|
||||||
|
|
||||||
|
return mapToPublicKeys(keys), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (PublicKeyStore) applyQueryFilter(
|
||||||
|
stmt squirrel.SelectBuilder,
|
||||||
|
filter *types.PublicKeyFilter,
|
||||||
|
) squirrel.SelectBuilder {
|
||||||
|
if filter.Query != "" {
|
||||||
|
stmt = stmt.Where("LOWER(public_key_identifier) LIKE ?",
|
||||||
|
fmt.Sprintf("%%%s%%", strings.ToLower(filter.Query)))
|
||||||
|
}
|
||||||
|
|
||||||
|
return stmt
|
||||||
|
}
|
||||||
|
|
||||||
|
func (PublicKeyStore) applySortFilter(
|
||||||
|
stmt squirrel.SelectBuilder,
|
||||||
|
filter *types.PublicKeyFilter,
|
||||||
|
) squirrel.SelectBuilder {
|
||||||
|
stmt = stmt.Limit(database.Limit(filter.Size))
|
||||||
|
stmt = stmt.Offset(database.Offset(filter.Page, filter.Size))
|
||||||
|
|
||||||
|
order := filter.Order
|
||||||
|
if order == enum.OrderDefault {
|
||||||
|
order = enum.OrderAsc
|
||||||
|
}
|
||||||
|
|
||||||
|
switch filter.Sort {
|
||||||
|
case enum.PublicKeySortIdentifier:
|
||||||
|
stmt = stmt.OrderBy("public_key_identifier " + order.String())
|
||||||
|
case enum.PublicKeySortCreated:
|
||||||
|
stmt = stmt.OrderBy("public_key_created " + order.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
return stmt
|
||||||
|
}
|
||||||
|
|
||||||
|
func mapToInternalPublicKey(in *types.PublicKey) publicKey {
|
||||||
|
return publicKey{
|
||||||
|
ID: in.ID,
|
||||||
|
PrincipalID: in.PrincipalID,
|
||||||
|
Created: in.Created,
|
||||||
|
Verified: null.IntFromPtr(in.Verified),
|
||||||
|
Identifier: in.Identifier,
|
||||||
|
Usage: string(in.Usage),
|
||||||
|
Fingerprint: in.Fingerprint,
|
||||||
|
Content: in.Content,
|
||||||
|
Comment: in.Comment,
|
||||||
|
Type: in.Type,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func mapToPublicKey(in *publicKey) types.PublicKey {
|
||||||
|
return types.PublicKey{
|
||||||
|
ID: in.ID,
|
||||||
|
PrincipalID: in.PrincipalID,
|
||||||
|
Created: in.Created,
|
||||||
|
Verified: in.Verified.Ptr(),
|
||||||
|
Identifier: in.Identifier,
|
||||||
|
Usage: enum.PublicKeyUsage(in.Usage),
|
||||||
|
Fingerprint: in.Fingerprint,
|
||||||
|
Content: in.Content,
|
||||||
|
Comment: in.Comment,
|
||||||
|
Type: in.Type,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func mapToPublicKeys(
|
||||||
|
keys []publicKey,
|
||||||
|
) []types.PublicKey {
|
||||||
|
res := make([]types.PublicKey, len(keys))
|
||||||
|
for i := 0; i < len(keys); i++ {
|
||||||
|
res[i] = mapToPublicKey(&keys[i])
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
|
@ -59,6 +59,7 @@ var WireSet = wire.NewSet(
|
||||||
ProvideTemplateStore,
|
ProvideTemplateStore,
|
||||||
ProvideTriggerStore,
|
ProvideTriggerStore,
|
||||||
ProvidePluginStore,
|
ProvidePluginStore,
|
||||||
|
ProvidePublicKeyStore,
|
||||||
)
|
)
|
||||||
|
|
||||||
// migrator is helper function to set up the database by performing automated
|
// migrator is helper function to set up the database by performing automated
|
||||||
|
@ -253,3 +254,8 @@ func ProvideSettingsStore(db *sqlx.DB) store.SettingsStore {
|
||||||
func ProvidePublicAccessStore(db *sqlx.DB) store.PublicAccessStore {
|
func ProvidePublicAccessStore(db *sqlx.DB) store.PublicAccessStore {
|
||||||
return NewPublicAccessStore(db)
|
return NewPublicAccessStore(db)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ProvidePublicKeyStore provides a public key store.
|
||||||
|
func ProvidePublicKeyStore(db *sqlx.DB) store.PublicKeyStore {
|
||||||
|
return NewPublicKeyStore(db)
|
||||||
|
}
|
||||||
|
|
|
@ -122,7 +122,8 @@ func initSystem(ctx context.Context, config *types.Config) (*server.System, erro
|
||||||
principalUIDTransformation := store.ProvidePrincipalUIDTransformation()
|
principalUIDTransformation := store.ProvidePrincipalUIDTransformation()
|
||||||
principalStore := database.ProvidePrincipalStore(db, principalUIDTransformation)
|
principalStore := database.ProvidePrincipalStore(db, principalUIDTransformation)
|
||||||
tokenStore := database.ProvideTokenStore(db)
|
tokenStore := database.ProvideTokenStore(db)
|
||||||
controller := user.ProvideController(transactor, principalUID, authorizer, principalStore, tokenStore, membershipStore)
|
publicKeyStore := database.ProvidePublicKeyStore(db)
|
||||||
|
controller := user.ProvideController(transactor, principalUID, authorizer, principalStore, tokenStore, membershipStore, publicKeyStore)
|
||||||
serviceController := service.NewController(principalUID, authorizer, principalStore)
|
serviceController := service.NewController(principalUID, authorizer, principalStore)
|
||||||
bootstrapBootstrap := bootstrap.ProvideBootstrap(config, controller, serviceController)
|
bootstrapBootstrap := bootstrap.ProvideBootstrap(config, controller, serviceController)
|
||||||
authenticator := authn.ProvideAuthenticator(config, principalStore, tokenStore)
|
authenticator := authn.ProvideAuthenticator(config, principalStore, tokenStore)
|
||||||
|
|
12
go.mod
12
go.mod
|
@ -23,6 +23,7 @@ require (
|
||||||
github.com/drone/spec v0.0.0-20230919004456-7455b8913ff5
|
github.com/drone/spec v0.0.0-20230919004456-7455b8913ff5
|
||||||
github.com/fatih/color v1.16.0
|
github.com/fatih/color v1.16.0
|
||||||
github.com/gabriel-vasile/mimetype v1.4.3
|
github.com/gabriel-vasile/mimetype v1.4.3
|
||||||
|
github.com/gliderlabs/ssh v0.3.7
|
||||||
github.com/go-chi/chi v1.5.4
|
github.com/go-chi/chi v1.5.4
|
||||||
github.com/go-chi/cors v1.2.1
|
github.com/go-chi/cors v1.2.1
|
||||||
github.com/go-redis/redis/v8 v8.11.5
|
github.com/go-redis/redis/v8 v8.11.5
|
||||||
|
@ -57,12 +58,12 @@ require (
|
||||||
github.com/zricethezav/gitleaks/v8 v8.18.2
|
github.com/zricethezav/gitleaks/v8 v8.18.2
|
||||||
go.starlark.net v0.0.0-20231121155337-90ade8b19d09
|
go.starlark.net v0.0.0-20231121155337-90ade8b19d09
|
||||||
go.uber.org/multierr v1.8.0
|
go.uber.org/multierr v1.8.0
|
||||||
golang.org/x/crypto v0.14.0
|
golang.org/x/crypto v0.17.0
|
||||||
golang.org/x/exp v0.0.0-20230108222341-4b8118a2686a
|
golang.org/x/exp v0.0.0-20230108222341-4b8118a2686a
|
||||||
golang.org/x/oauth2 v0.10.0
|
golang.org/x/oauth2 v0.10.0
|
||||||
golang.org/x/sync v0.3.0
|
golang.org/x/sync v0.3.0
|
||||||
golang.org/x/term v0.13.0
|
golang.org/x/term v0.15.0
|
||||||
golang.org/x/text v0.13.0
|
golang.org/x/text v0.14.0
|
||||||
google.golang.org/api v0.132.0
|
google.golang.org/api v0.132.0
|
||||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6
|
gopkg.in/alecthomas/kingpin.v2 v2.2.6
|
||||||
gopkg.in/mail.v2 v2.3.1
|
gopkg.in/mail.v2 v2.3.1
|
||||||
|
@ -76,6 +77,7 @@ require (
|
||||||
dario.cat/mergo v1.0.0 // indirect
|
dario.cat/mergo v1.0.0 // indirect
|
||||||
github.com/99designs/httpsignatures-go v0.0.0-20170731043157-88528bf4ca7e // indirect
|
github.com/99designs/httpsignatures-go v0.0.0-20170731043157-88528bf4ca7e // indirect
|
||||||
github.com/BobuSumisu/aho-corasick v1.0.3 // indirect
|
github.com/BobuSumisu/aho-corasick v1.0.3 // indirect
|
||||||
|
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
|
||||||
github.com/antonmedv/expr v1.15.2 // indirect
|
github.com/antonmedv/expr v1.15.2 // indirect
|
||||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
|
@ -165,14 +167,14 @@ require (
|
||||||
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect
|
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/sergi/go-diff v1.3.1 // indirect
|
github.com/sergi/go-diff v1.3.1 // indirect
|
||||||
github.com/swaggest/jsonschema-go v0.3.40 // indirect
|
github.com/swaggest/jsonschema-go v0.3.40
|
||||||
github.com/swaggest/refl v1.1.0 // indirect
|
github.com/swaggest/refl v1.1.0 // indirect
|
||||||
github.com/vearutop/statigz v1.4.0 // indirect
|
github.com/vearutop/statigz v1.4.0 // indirect
|
||||||
github.com/yuin/goldmark v1.4.13
|
github.com/yuin/goldmark v1.4.13
|
||||||
go.uber.org/atomic v1.10.0 // indirect
|
go.uber.org/atomic v1.10.0 // indirect
|
||||||
golang.org/x/mod v0.12.0 // indirect
|
golang.org/x/mod v0.12.0 // indirect
|
||||||
golang.org/x/net v0.17.0 // indirect
|
golang.org/x/net v0.17.0 // indirect
|
||||||
golang.org/x/sys v0.14.0 // indirect
|
golang.org/x/sys v0.15.0 // indirect
|
||||||
golang.org/x/tools v0.13.0 // indirect
|
golang.org/x/tools v0.13.0 // indirect
|
||||||
google.golang.org/genproto v0.0.0-20230706204954-ccb25ca9f130 // indirect
|
google.golang.org/genproto v0.0.0-20230706204954-ccb25ca9f130 // indirect
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
|
|
31
go.sum
31
go.sum
|
@ -81,6 +81,8 @@ github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRF
|
||||||
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc=
|
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc=
|
||||||
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE=
|
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE=
|
||||||
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
|
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
|
||||||
|
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
|
||||||
|
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
|
||||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||||
github.com/antonmedv/expr v1.15.2 h1:afFXpDWIC2n3bF+kTZE1JvFo+c34uaM3sTqh8z0xfdU=
|
github.com/antonmedv/expr v1.15.2 h1:afFXpDWIC2n3bF+kTZE1JvFo+c34uaM3sTqh8z0xfdU=
|
||||||
github.com/antonmedv/expr v1.15.2/go.mod h1:0E/6TxnOlRNp81GMzX9QfDPAmHo2Phg00y4JUv1ihsE=
|
github.com/antonmedv/expr v1.15.2/go.mod h1:0E/6TxnOlRNp81GMzX9QfDPAmHo2Phg00y4JUv1ihsE=
|
||||||
|
@ -194,10 +196,6 @@ github.com/drone/go-convert v0.0.0-20230919093251-7104c3bcc635 h1:qQX+U2iEm4X2Fc
|
||||||
github.com/drone/go-convert v0.0.0-20230919093251-7104c3bcc635/go.mod h1:PyCDcuAhGF6W0VJ6qMmlM47dsSyGv/zDiMqeJxMFuGM=
|
github.com/drone/go-convert v0.0.0-20230919093251-7104c3bcc635/go.mod h1:PyCDcuAhGF6W0VJ6qMmlM47dsSyGv/zDiMqeJxMFuGM=
|
||||||
github.com/drone/go-generate v0.0.0-20230920014042-6085ee5c9522 h1:i3EfRpr/eYifK9w0ninT3xHAthkS4NTQjLX0/zDIsy4=
|
github.com/drone/go-generate v0.0.0-20230920014042-6085ee5c9522 h1:i3EfRpr/eYifK9w0ninT3xHAthkS4NTQjLX0/zDIsy4=
|
||||||
github.com/drone/go-generate v0.0.0-20230920014042-6085ee5c9522/go.mod h1:eTfy716efMJgVvk/ZkRvitaXY2UuytfqDjxclFMeLdQ=
|
github.com/drone/go-generate v0.0.0-20230920014042-6085ee5c9522/go.mod h1:eTfy716efMJgVvk/ZkRvitaXY2UuytfqDjxclFMeLdQ=
|
||||||
github.com/drone/go-scm v1.31.2 h1:6hZxf0aETV17830fMCPrgcA4y8j/8Gdfy0xEdInUeqQ=
|
|
||||||
github.com/drone/go-scm v1.31.2/go.mod h1:DFIJJjhMj0TSXPz+0ni4nyZ9gtTtC40Vh/TGRugtyWw=
|
|
||||||
github.com/drone/go-scm v1.36.0 h1:5d9lJVoXGJvLqG2CFwLcZvMMiZPNxCrmHbxRVzu19qM=
|
|
||||||
github.com/drone/go-scm v1.36.0/go.mod h1:DFIJJjhMj0TSXPz+0ni4nyZ9gtTtC40Vh/TGRugtyWw=
|
|
||||||
github.com/drone/go-scm v1.37.1 h1:U42+3JRFvmBJXZnKqphF377h2mT9Pe+Oin/lDD1fnJ8=
|
github.com/drone/go-scm v1.37.1 h1:U42+3JRFvmBJXZnKqphF377h2mT9Pe+Oin/lDD1fnJ8=
|
||||||
github.com/drone/go-scm v1.37.1/go.mod h1:DFIJJjhMj0TSXPz+0ni4nyZ9gtTtC40Vh/TGRugtyWw=
|
github.com/drone/go-scm v1.37.1/go.mod h1:DFIJJjhMj0TSXPz+0ni4nyZ9gtTtC40Vh/TGRugtyWw=
|
||||||
github.com/drone/runner-go v1.12.0 h1:zUjDj9ylsJ4n4Mvy4znddq/Z4EBzcUXzTltpzokKtgs=
|
github.com/drone/runner-go v1.12.0 h1:zUjDj9ylsJ4n4Mvy4znddq/Z4EBzcUXzTltpzokKtgs=
|
||||||
|
@ -236,6 +234,8 @@ github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
||||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
github.com/gitleaks/go-gitdiff v0.9.0 h1:SHAU2l0ZBEo8g82EeFewhVy81sb7JCxW76oSPtR/Nqg=
|
github.com/gitleaks/go-gitdiff v0.9.0 h1:SHAU2l0ZBEo8g82EeFewhVy81sb7JCxW76oSPtR/Nqg=
|
||||||
github.com/gitleaks/go-gitdiff v0.9.0/go.mod h1:pKz0X4YzCKZs30BL+weqBIG7mx0jl4tF1uXV9ZyNvrA=
|
github.com/gitleaks/go-gitdiff v0.9.0/go.mod h1:pKz0X4YzCKZs30BL+weqBIG7mx0jl4tF1uXV9ZyNvrA=
|
||||||
|
github.com/gliderlabs/ssh v0.3.7 h1:iV3Bqi942d9huXnzEF2Mt+CY9gLu8DNM4Obd+8bODRE=
|
||||||
|
github.com/gliderlabs/ssh v0.3.7/go.mod h1:zpHEXBstFnQYtGnB8k8kQLol82umzn/2/snG7alWVD8=
|
||||||
github.com/go-chi/chi v1.5.4 h1:QHdzF2szwjqVV4wmByUnTcsbIg7UGaQ0tPF2t5GcAIs=
|
github.com/go-chi/chi v1.5.4 h1:QHdzF2szwjqVV4wmByUnTcsbIg7UGaQ0tPF2t5GcAIs=
|
||||||
github.com/go-chi/chi v1.5.4/go.mod h1:uaf8YgoFazUOkPBG7fxPftUylNumIev9awIWOENIuEg=
|
github.com/go-chi/chi v1.5.4/go.mod h1:uaf8YgoFazUOkPBG7fxPftUylNumIev9awIWOENIuEg=
|
||||||
github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4=
|
github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4=
|
||||||
|
@ -249,7 +249,6 @@ github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgO
|
||||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||||
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
|
|
||||||
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
|
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
|
||||||
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||||
github.com/go-logr/zerologr v1.2.3 h1:up5N9vcH9Xck3jJkXzgyOxozT14R47IyDODz8LM1KSs=
|
github.com/go-logr/zerologr v1.2.3 h1:up5N9vcH9Xck3jJkXzgyOxozT14R47IyDODz8LM1KSs=
|
||||||
|
@ -299,7 +298,6 @@ github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt
|
||||||
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
||||||
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
|
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
|
||||||
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
||||||
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
@ -509,19 +507,16 @@ github.com/jmoiron/sqlx v1.3.3/go.mod h1:2BljVx/86SuTyjE+aPYlHCTNvZrnJXghYGpNiXL
|
||||||
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
|
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
|
||||||
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
||||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
|
||||||
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||||
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
|
||||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
|
||||||
github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8=
|
github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8=
|
||||||
github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=
|
github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=
|
||||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||||
|
@ -608,7 +603,6 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJ
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
|
||||||
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
||||||
github.com/muesli/reflow v0.2.1-0.20210115123740-9e1d0d53df68 h1:y1p/ycavWjGT9FnmSjdbWUlLGvcxrY0Rw3ATltrxOhk=
|
github.com/muesli/reflow v0.2.1-0.20210115123740-9e1d0d53df68 h1:y1p/ycavWjGT9FnmSjdbWUlLGvcxrY0Rw3ATltrxOhk=
|
||||||
github.com/muesli/reflow v0.2.1-0.20210115123740-9e1d0d53df68/go.mod h1:Xk+z4oIWdQqJzsxyjgl3P22oYZnHdZ8FFTHAQQt5BMQ=
|
github.com/muesli/reflow v0.2.1-0.20210115123740-9e1d0d53df68/go.mod h1:Xk+z4oIWdQqJzsxyjgl3P22oYZnHdZ8FFTHAQQt5BMQ=
|
||||||
|
@ -616,7 +610,6 @@ github.com/muesli/termenv v0.11.1-0.20220204035834-5ac8409525e0/go.mod h1:Bd5NYQ
|
||||||
github.com/muesli/termenv v0.15.1 h1:UzuTb/+hhlBugQz28rpzey4ZuKcZ03MeKsoG7IJZIxs=
|
github.com/muesli/termenv v0.15.1 h1:UzuTb/+hhlBugQz28rpzey4ZuKcZ03MeKsoG7IJZIxs=
|
||||||
github.com/muesli/termenv v0.15.1/go.mod h1:HeAQPTzpfs016yGtA4g00CsdYnVLJvxsS4ANqrZs2sQ=
|
github.com/muesli/termenv v0.15.1/go.mod h1:HeAQPTzpfs016yGtA4g00CsdYnVLJvxsS4ANqrZs2sQ=
|
||||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
|
||||||
github.com/natessilva/dag v0.0.0-20180124060714-7194b8dcc5c4 h1:dnMxwus89s86tI8rcGVp2HwZzlz7c5o92VOy7dSckBQ=
|
github.com/natessilva/dag v0.0.0-20180124060714-7194b8dcc5c4 h1:dnMxwus89s86tI8rcGVp2HwZzlz7c5o92VOy7dSckBQ=
|
||||||
github.com/natessilva/dag v0.0.0-20180124060714-7194b8dcc5c4/go.mod h1:cojhOHk1gbMeklOyDP2oKKLftefXoJreOQGOrXk+Z38=
|
github.com/natessilva/dag v0.0.0-20180124060714-7194b8dcc5c4/go.mod h1:cojhOHk1gbMeklOyDP2oKKLftefXoJreOQGOrXk+Z38=
|
||||||
github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
|
github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
|
||||||
|
@ -878,8 +871,8 @@ golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5
|
||||||
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
|
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
|
||||||
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||||
|
@ -1078,14 +1071,14 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
|
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
||||||
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=
|
golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
|
||||||
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
|
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
|
||||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
@ -1097,8 +1090,8 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed 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 enum
|
||||||
|
|
||||||
|
// PublicKeyUsage represents usage type of public key.
|
||||||
|
type PublicKeyUsage string
|
||||||
|
|
||||||
|
// PublicKeyUsage enumeration.
|
||||||
|
const (
|
||||||
|
PublicKeyUsageAuth PublicKeyUsage = "auth"
|
||||||
|
PublicKeyUsageSign PublicKeyUsage = "sign"
|
||||||
|
)
|
||||||
|
|
||||||
|
var publicKeyTypes = sortEnum([]PublicKeyUsage{
|
||||||
|
PublicKeyUsageAuth,
|
||||||
|
})
|
||||||
|
|
||||||
|
func (PublicKeyUsage) Enum() []interface{} { return toInterfaceSlice(publicKeyTypes) }
|
||||||
|
func (s PublicKeyUsage) Sanitize() (PublicKeyUsage, bool) {
|
||||||
|
return Sanitize(s, GetAllPublicKeyUsages)
|
||||||
|
}
|
||||||
|
func GetAllPublicKeyUsages() ([]PublicKeyUsage, PublicKeyUsage) {
|
||||||
|
return publicKeyTypes, PublicKeyUsageAuth
|
||||||
|
}
|
||||||
|
|
||||||
|
// PublicKeySort is used to specify sorting of public keys.
|
||||||
|
type PublicKeySort string
|
||||||
|
|
||||||
|
// PublicKeySort enumeration.
|
||||||
|
const (
|
||||||
|
PublicKeySortCreated PublicKeySort = "created"
|
||||||
|
PublicKeySortIdentifier PublicKeySort = "identifier"
|
||||||
|
)
|
||||||
|
|
||||||
|
var publicKeySorts = sortEnum([]PublicKeySort{
|
||||||
|
PublicKeySortCreated,
|
||||||
|
PublicKeySortIdentifier,
|
||||||
|
})
|
||||||
|
|
||||||
|
func (PublicKeySort) Enum() []interface{} { return toInterfaceSlice(publicKeySorts) }
|
||||||
|
func (s PublicKeySort) Sanitize() (PublicKeySort, bool) { return Sanitize(s, GetAllPublicKeySorts) }
|
||||||
|
func GetAllPublicKeySorts() ([]PublicKeySort, PublicKeySort) {
|
||||||
|
return publicKeySorts, PublicKeySortCreated
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed 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 types
|
||||||
|
|
||||||
|
import "github.com/harness/gitness/types/enum"
|
||||||
|
|
||||||
|
type PublicKey struct {
|
||||||
|
ID int64 `json:"-"` // frontend doesn't need it
|
||||||
|
PrincipalID int64 `json:"-"` // API always returns keys for the same user
|
||||||
|
Created int64 `json:"created"`
|
||||||
|
Verified *int64 `json:"verified"`
|
||||||
|
Identifier string `json:"identifier"`
|
||||||
|
Usage enum.PublicKeyUsage `json:"usage"`
|
||||||
|
Fingerprint string `json:"fingerprint"`
|
||||||
|
Content string `json:"-"`
|
||||||
|
Comment string `json:"comment"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PublicKeyFilter struct {
|
||||||
|
ListQueryFilter
|
||||||
|
Sort enum.PublicKeySort
|
||||||
|
Order enum.Order
|
||||||
|
}
|
Loading…
Reference in New Issue