Skip to content

Commit

Permalink
feat: Allow user to copy secret value
Browse files Browse the repository at this point in the history
  • Loading branch information
kuzznya committed Feb 26, 2024
1 parent f7f1d06 commit 0f744ae
Show file tree
Hide file tree
Showing 6 changed files with 92 additions and 35 deletions.
38 changes: 31 additions & 7 deletions api/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -285,13 +285,7 @@ paths:
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/Secret'
- properties:
value:
type: string
required:
- value
$ref: '#/components/schemas/SecretValue'
responses:
200:
description: Created project secret
Expand Down Expand Up @@ -320,6 +314,26 @@ paths:
required: true
schema:
$ref: '#/components/schemas/SecretName'
get:
operationId: GetSecretValue
tags:
- project
summary: Get secret
responses:
200:
description: Secret with value
content:
application/json:
schema:
$ref: '#/components/schemas/SecretValue'
400:
$ref: '#/components/responses/BadRequest'
401:
$ref: '#/components/responses/Unauthorized'
404:
$ref: '#/components/responses/NotFound'
500:
$ref: '#/components/responses/InternalServerError'
delete:
operationId: DeleteSecret
tags:
Expand Down Expand Up @@ -1017,6 +1031,16 @@ components:
required:
- name

SecretValue:
allOf:
- $ref: '#/components/schemas/Secret'
- type: object
properties:
value:
type: string
required:
- value

ApiKey:
type: object
properties:
Expand Down
35 changes: 26 additions & 9 deletions app/core/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ type Projects interface {
JoinProject(ctx context.Context, code string, auth middleware.Authentication) (*openapi.Project, error)
RegenerateInviteCode(ctx context.Context, id string, auth middleware.Authentication) (string, error)
GetSecrets(projectId string, auth middleware.Authentication) ([]openapi.Secret, error)
CreateSecret(ctx context.Context, projectId string, secret openapi.Secret, value string, auth middleware.Authentication) (*openapi.Secret, error)
CreateSecret(ctx context.Context, projectId string, secretValue openapi.SecretValue, auth middleware.Authentication) (*openapi.Secret, error)
GetSecretValue(projectId string, name string, auth middleware.Authentication) (*openapi.SecretValue, error)
DeleteSecret(ctx context.Context, projectId string, name string, auth middleware.Authentication) error
checkAccess(id string, auth middleware.Authentication) error
}
Expand Down Expand Up @@ -320,30 +321,30 @@ func (p projectsImpl) GetSecrets(projectId string, auth middleware.Authenticatio
return secrets, nil
}

func (p projectsImpl) CreateSecret(ctx context.Context, projectId string, secret openapi.Secret, value string, auth middleware.Authentication) (*openapi.Secret, error) {
func (p projectsImpl) CreateSecret(ctx context.Context, projectId string, secretValue openapi.SecretValue, auth middleware.Authentication) (*openapi.Secret, error) {
if err := p.checkAccess(projectId, auth); err != nil {
return nil, err
}
exists, err := p.storage.SecretRepository().ExistsByProjectIdAndName(projectId, secret.Name)
exists, err := p.storage.SecretRepository().ExistsByProjectIdAndName(projectId, secretValue.Name)
if err != nil {
return nil, errors.Wrap(err, "failed to check if secret already exists")
}
if exists {
return nil, apperrors.BadRequest(fmt.Sprintf("Secret %s already exists in the project", secret.Name))
return nil, apperrors.BadRequest(fmt.Sprintf("Secret %s already exists in the project", secretValue.Name))
}
entity := storage.SecretEntity{
ProjectId: projectId,
Name: secret.Name,
Value: value,
Name: secretValue.Name,
Value: secretValue.Value,
}
err = p.storage.ExecTx(ctx, func(s *storage.Storage) error {
err := s.SecretRepository().CreateNew(entity)
if err != nil {
return err
}

config := applyConfigsV1.Secret(strings.ReplaceAll(strings.ToLower(secret.Name), "_", "-"), projectId).
WithStringData(map[string]string{secretKey: value})
config := applyConfigsV1.Secret(strings.ReplaceAll(strings.ToLower(secretValue.Name), "_", "-"), projectId).
WithStringData(map[string]string{secretKey: secretValue.Value})
_, err = p.clientset.CoreV1().Secrets(projectId).Apply(ctx, config, metav1.ApplyOptions{FieldManager: "letsdeploy"})
if err != nil {
return err
Expand All @@ -353,10 +354,26 @@ func (p projectsImpl) CreateSecret(ctx context.Context, projectId string, secret
if err != nil {
return nil, errors.Wrap(err, "failed to create new secret")
}
log.Infof("Created secret %s in project %s", secret.Name, projectId)
log.Infof("Created secret %s in project %s", secretValue.Name, projectId)
secret := openapi.Secret{Name: secretValue.Name}
return &secret, nil
}

func (p projectsImpl) GetSecretValue(projectId string, name string, auth middleware.Authentication) (*openapi.SecretValue, error) {
if err := p.checkAccess(projectId, auth); err != nil {
return nil, err
}
secret, err := p.storage.SecretRepository().FindByProjectIdAndName(projectId, name)
if err != nil {
return nil, errors.Wrap(err, "failed to retrieve secret")
}
return &openapi.SecretValue{
Name: secret.Name,
Value: secret.Value,
ManagedServiceId: secret.ManagedServiceId,
}, nil
}

func (p projectsImpl) DeleteSecret(ctx context.Context, projectId string, name string, auth middleware.Authentication) error {
if err := p.checkAccess(projectId, auth); err != nil {
return err
Expand Down
15 changes: 9 additions & 6 deletions app/server/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,18 +88,21 @@ func (s Server) GetSecrets(ctx context.Context, request openapi.GetSecretsReques
}

func (s Server) CreateSecret(ctx context.Context, request openapi.CreateSecretRequestObject) (openapi.CreateSecretResponseObject, error) {
secret, err := s.core.Projects.CreateSecret(
ctx,
request.Id,
openapi.Secret{Name: request.Body.Name},
request.Body.Value,
middleware.GetAuth(ctx))
secret, err := s.core.Projects.CreateSecret(ctx, request.Id, *request.Body, middleware.GetAuth(ctx))
if err != nil {
return nil, errors.Wrap(err, "failed to create new secret")
}
return openapi.CreateSecret200JSONResponse(*secret), nil
}

func (s Server) GetSecretValue(ctx context.Context, request openapi.GetSecretValueRequestObject) (openapi.GetSecretValueResponseObject, error) {
secretValue, err := s.core.Projects.GetSecretValue(request.Id, request.Name, middleware.GetAuth(ctx))
if err != nil {
return nil, err
}
return openapi.GetSecretValue200JSONResponse(*secretValue), nil
}

func (s Server) DeleteSecret(ctx context.Context, request openapi.DeleteSecretRequestObject) (openapi.DeleteSecretResponseObject, error) {
err := s.core.Projects.DeleteSecret(ctx, request.Id, request.Name, middleware.GetAuth(ctx))
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/MongoDbConfig.vue
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,7 @@ loadUsers();

<b-col cols="3" class="text-end">
<b-button
class="mr-2"
class="mx-1 mb-1"
variant="outline-danger"
@click.stop="onDeleteUserClicked(user)"
>
Expand Down
31 changes: 24 additions & 7 deletions frontend/src/views/ProjectView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,18 @@ async function deleteService() {
}
}
async function copySecretValue(secret: string) {
try {
const secretValue = await api.ProjectApi.getSecretValue(
props.id,
secret
).then((r) => r.data);
await navigator.clipboard.writeText(secretValue.value);
} catch (e: Error | any) {
error.value = e;
}
}
const deleteSecretDialogEnabled = ref(false);
const secretToDelete = ref<string | null>(null);
Expand Down Expand Up @@ -482,7 +494,7 @@ function cancelSecretCreation() {

<b-col cols="3" class="text-end">
<b-button
class="mr-2"
class="mx-1 mb-1"
variant="outline-danger"
@click.stop="onDeleteManagedServiceClicked(managedService)"
>
Expand Down Expand Up @@ -610,13 +622,18 @@ function cancelSecretCreation() {
</b-row>
</b-col>

<b-col
v-if="secret.managedService == null"
cols="3"
class="text-end"
>
<b-col cols="3" class="text-end">
<b-button
class="mx-1 mb-1"
variant="outline-secondary"
@click.stop="copySecretValue(secret.name)"
>
<i class="bi bi-copy" />
</b-button>

<b-button
class="mr-2"
v-if="secret.managedService == null"
class="mx-1 mb-1"
variant="outline-danger"
@click.stop="onDeleteSecretClicked(secret.name)"
>
Expand Down
6 changes: 1 addition & 5 deletions frontend/src/views/ProjectsView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -187,18 +187,14 @@ async function projectParticipants(id: string) {

<b-col class="text-end" cols="3">
<b-button
class="mr-2"
class="mx-1 mb-1"
variant="outline-danger"
@click.stop="onDeleteProjectClicked(project.id)"
>
<i class="bi bi-trash"></i>
</b-button>
</b-col>
</b-row>

<b-row>

</b-row>
</b-card>
</b-col>
</b-row>
Expand Down

0 comments on commit 0f744ae

Please sign in to comment.