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
|
||||
tokenStore store.TokenStore
|
||||
membershipStore store.MembershipStore
|
||||
publicKeyStore store.PublicKeyStore
|
||||
}
|
||||
|
||||
func NewController(
|
||||
|
@ -43,6 +44,7 @@ func NewController(
|
|||
principalStore store.PrincipalStore,
|
||||
tokenStore store.TokenStore,
|
||||
membershipStore store.MembershipStore,
|
||||
publicKeyStore store.PublicKeyStore,
|
||||
) *Controller {
|
||||
return &Controller{
|
||||
tx: tx,
|
||||
|
@ -51,6 +53,7 @@ func NewController(
|
|||
principalStore: principalStore,
|
||||
tokenStore: tokenStore,
|
||||
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,
|
||||
tokenStore store.TokenStore,
|
||||
membershipStore store.MembershipStore,
|
||||
publicKeyStore store.PublicKeyStore,
|
||||
) *Controller {
|
||||
return NewController(
|
||||
tx,
|
||||
|
@ -42,5 +43,6 @@ func ProvideController(
|
|||
authorizer,
|
||||
principalStore,
|
||||
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
|
||||
// for user account resources.
|
||||
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(usererror.Error), http.StatusInternalServerError)
|
||||
_ = 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))
|
||||
})
|
||||
})
|
||||
|
||||
// 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(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,
|
||||
ProvideTriggerStore,
|
||||
ProvidePluginStore,
|
||||
ProvidePublicKeyStore,
|
||||
)
|
||||
|
||||
// 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 {
|
||||
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()
|
||||
principalStore := database.ProvidePrincipalStore(db, principalUIDTransformation)
|
||||
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)
|
||||
bootstrapBootstrap := bootstrap.ProvideBootstrap(config, controller, serviceController)
|
||||
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/fatih/color v1.16.0
|
||||
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/cors v1.2.1
|
||||
github.com/go-redis/redis/v8 v8.11.5
|
||||
|
@ -57,12 +58,12 @@ require (
|
|||
github.com/zricethezav/gitleaks/v8 v8.18.2
|
||||
go.starlark.net v0.0.0-20231121155337-90ade8b19d09
|
||||
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/oauth2 v0.10.0
|
||||
golang.org/x/sync v0.3.0
|
||||
golang.org/x/term v0.13.0
|
||||
golang.org/x/text v0.13.0
|
||||
golang.org/x/term v0.15.0
|
||||
golang.org/x/text v0.14.0
|
||||
google.golang.org/api v0.132.0
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6
|
||||
gopkg.in/mail.v2 v2.3.1
|
||||
|
@ -76,6 +77,7 @@ require (
|
|||
dario.cat/mergo v1.0.0 // indirect
|
||||
github.com/99designs/httpsignatures-go v0.0.0-20170731043157-88528bf4ca7e // 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/aymanbagabas/go-osc52/v2 v2.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/pmezard/go-difflib v1.0.0 // 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/vearutop/statigz v1.4.0 // indirect
|
||||
github.com/yuin/goldmark v1.4.13
|
||||
go.uber.org/atomic v1.10.0 // indirect
|
||||
golang.org/x/mod v0.12.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
|
||||
google.golang.org/genproto v0.0.0-20230706204954-ccb25ca9f130 // 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/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE=
|
||||
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/antonmedv/expr v1.15.2 h1:afFXpDWIC2n3bF+kTZE1JvFo+c34uaM3sTqh8z0xfdU=
|
||||
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-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-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/go.mod h1:DFIJJjhMj0TSXPz+0ni4nyZ9gtTtC40Vh/TGRugtyWw=
|
||||
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/gitleaks/go-gitdiff v0.9.0 h1:SHAU2l0ZBEo8g82EeFewhVy81sb7JCxW76oSPtR/Nqg=
|
||||
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/go.mod h1:uaf8YgoFazUOkPBG7fxPftUylNumIev9awIWOENIuEg=
|
||||
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.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.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/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
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.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
|
||||
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.3.1/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/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
||||
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.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.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.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.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/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
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/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=
|
||||
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/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.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
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/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/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-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/go.mod h1:cojhOHk1gbMeklOyDP2oKKLftefXoJreOQGOrXk+Z38=
|
||||
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-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.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
|
||||
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
|
||||
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-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
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.1.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.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
||||
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-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.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=
|
||||
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
|
||||
golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
|
||||
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.3.0/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.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||
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.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
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-20181108054448-85acf8d2951c/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