diff --git a/controllers/environment_controller.go b/controllers/environment_controller.go new file mode 100644 index 0000000..1fdcd92 --- /dev/null +++ b/controllers/environment_controller.go @@ -0,0 +1,167 @@ +package controllers + +import ( + "errors" + "net/http" + "osvauld/customerrors" + dto "osvauld/dtos" + "osvauld/service" + "osvauld/utils" + + "github.com/gin-gonic/gin" + "github.com/google/uuid" +) + +func AddEnvironment(ctx *gin.Context) { + caller, err := utils.FetchUserIDFromCtx(ctx) + if err != nil { + SendResponse(ctx, http.StatusBadRequest, nil, "", errors.New("invalid user id")) + return + } + var req dto.AddEnvironment + if err := ctx.ShouldBindJSON(&req); err != nil { + SendResponse(ctx, http.StatusBadRequest, nil, "", err) + return + } + _, err = service.AddEnvironment(ctx, req, caller) + if err != nil { + SendResponse(ctx, http.StatusInternalServerError, nil, "", err) + return + } + SendResponse(ctx, http.StatusOK, nil, "added environment", nil) +} + +func GetEnvironments(ctx *gin.Context) { + caller, err := utils.FetchUserIDFromCtx(ctx) + if err != nil { + SendResponse(ctx, http.StatusBadRequest, nil, "", errors.New("invalid user id")) + return + } + environments, err := service.GetEnvironments(ctx, caller) + if err != nil { + SendResponse(ctx, http.StatusInternalServerError, nil, "", err) + return + } + SendResponse(ctx, http.StatusOK, environments, "fetched environments", nil) +} + +func GetEnvironmentFields(ctx *gin.Context) { + // caller, err := utils.FetchUserIDFromCtx(ctx) + // if err != nil { + // SendResponse(ctx, http.StatusBadRequest, nil, "", errors.New("invalid user id")) + // return + // } + environmentIDStr := ctx.Param("id") + environmentID, err := uuid.Parse(environmentIDStr) + if err != nil { + SendResponse(ctx, http.StatusBadRequest, nil, "", errors.New("invalid environment id")) + return + } + // TODO: Add check for user access to environment + credentials, err := service.GetEnvironmentFields(ctx, environmentID) + if err != nil { + SendResponse(ctx, http.StatusInternalServerError, nil, "", err) + return + } + SendResponse(ctx, http.StatusOK, credentials, "fetched credentials", nil) +} + +func GetEnvironmentByName(ctx *gin.Context) { + caller, err := utils.FetchUserIDFromCtx(ctx) + if err != nil { + SendResponse(ctx, http.StatusBadRequest, nil, "", errors.New("invalid user id")) + return + } + environmentName := ctx.Param("name") + environment, err := service.GetEnvironmentByName(ctx, environmentName, caller) + if err != nil { + SendResponse(ctx, http.StatusInternalServerError, nil, "", errors.New("failed to fetch environment")) + return + } + SendResponse(ctx, http.StatusOK, environment, "Fetched environment", nil) +} + +func EditEnvironmentFieldName(ctx *gin.Context) { + caller, err := utils.FetchUserIDFromCtx(ctx) + if err != nil { + SendResponse(ctx, http.StatusBadRequest, nil, "", errors.New("invalid user id")) + return + } + + var req dto.EditEnvFieldName + if err := ctx.ShouldBindJSON(&req); err != nil { + SendResponse(ctx, http.StatusBadRequest, nil, "", err) + return + } + + response, err := service.EditEnvFieldName(ctx, req, caller) + if err != nil { + if _, ok := err.(*customerrors.UserDoesNotHaveEnvironmentAccess); ok { + SendResponse(ctx, http.StatusUnauthorized, nil, "", err) + return + } + SendResponse(ctx, http.StatusInternalServerError, nil, "", err) + return + } + SendResponse(ctx, http.StatusOK, response, "", nil) + +} + +func GetCredentialEnvFieldsForEditDataSync(ctx *gin.Context) { + userID, err := utils.FetchUserIDFromCtx(ctx) + if err != nil { + SendResponse(ctx, http.StatusUnauthorized, nil, "", errors.New("unauthorized")) + return + } + + credentialIDStr := ctx.Param("credentialId") + credentailID, err := uuid.Parse(credentialIDStr) + if err != nil { + SendResponse(ctx, http.StatusBadRequest, nil, "", errors.New("invalid credential id")) + return + } + + fieldData, err := service.GetCredentialEnvFieldsForEditDataSync(ctx, credentailID, userID) + if err != nil { + + if _, ok := err.(*customerrors.UserDoesNotHaveCredentialAccessError); ok { + SendResponse(ctx, http.StatusUnauthorized, nil, "", err) + return + } + + SendResponse(ctx, http.StatusInternalServerError, nil, "", err) + return + } + + SendResponse(ctx, http.StatusOK, fieldData, "Fetched Fields", nil) + +} + +func GetEnvsForCredential(ctx *gin.Context) { + // userID, err := utils.FetchUserIDFromCtx(ctx) + // if err != nil { + // SendResponse(ctx, http.StatusUnauthorized, nil, "", errors.New("unauthorized")) + // return + // } + + credentialIDStr := ctx.Param("credentialId") + credentailID, err := uuid.Parse(credentialIDStr) + if err != nil { + SendResponse(ctx, http.StatusBadRequest, nil, "", errors.New("invalid credential id")) + return + } + + envs, err := service.GetEnvsForCredential(ctx, credentailID) + if err != nil { + + if _, ok := err.(*customerrors.UserDoesNotHaveCredentialAccessError); ok { + SendResponse(ctx, http.StatusUnauthorized, nil, "", err) + return + } + + SendResponse(ctx, http.StatusInternalServerError, nil, "", err) + return + } + + SendResponse(ctx, http.StatusOK, envs, "Fetched Environments", nil) +} diff --git a/controllers/folder_controller.go b/controllers/folder_controller.go index 03fa5ed..7111746 100644 --- a/controllers/folder_controller.go +++ b/controllers/folder_controller.go @@ -27,6 +27,7 @@ func CreateFolder(ctx *gin.Context) { folderDetails, err := service.CreateFolder(ctx, req, caller) if err != nil { + logger.Errorf(err.Error()) SendResponse(ctx, http.StatusInternalServerError, nil, "", errors.New("failed to create folder")) return } @@ -67,6 +68,7 @@ func RemoveFolder(ctx *gin.Context) { } err = service.RemoveFolder(ctx, folderID, caller) if err != nil { + logger.Debugf(err.Error()) SendResponse(ctx, http.StatusInternalServerError, nil, "", errors.New("failed to remove folder")) return } diff --git a/controllers/share_credential_controller.go b/controllers/share_credential_controller.go index 2d04ade..d6d9c1a 100644 --- a/controllers/share_credential_controller.go +++ b/controllers/share_credential_controller.go @@ -116,3 +116,27 @@ func ShareFolderWithGroups(ctx *gin.Context) { } SendResponse(ctx, http.StatusOK, response, "Success", nil) } + +func ShareCredentialsWithEnvironment(ctx *gin.Context) { + + caller, err := utils.FetchUserIDFromCtx(ctx) + if err != nil { + SendResponse(ctx, http.StatusUnauthorized, nil, "", errors.New("unauthorized")) + return + } + + var req dto.ShareCredentialsWithEnvironmentRequest + if err := ctx.ShouldBindJSON(&req); err != nil { + SendResponse(ctx, http.StatusBadRequest, nil, "", err) + return + } + + err = service.ShareCredentialsWithEnvironment(ctx, req, caller) + if err != nil { + + SendResponse(ctx, http.StatusInternalServerError, nil, "", err) + return + } + SendResponse(ctx, http.StatusOK, nil, "Success", nil) + +} diff --git a/controllers/user_controller.go b/controllers/user_controller.go index 47fe01c..ae81382 100644 --- a/controllers/user_controller.go +++ b/controllers/user_controller.go @@ -28,7 +28,7 @@ func CreateUser(ctx *gin.Context) { SendResponse(ctx, http.StatusInternalServerError, nil, "invalid user type", err) return } - if userType != "admin" { + if userType != "admin" && userType != "superadmin" { SendResponse(ctx, http.StatusUnauthorized, nil, "user not authorized", errors.New("user not authorized")) return } @@ -91,6 +91,7 @@ func Register(ctx *gin.Context) { func GetChallenge(ctx *gin.Context) { var req dto.CreateChallenge + logger.Infof("%v", req) if err := ctx.ShouldBindJSON(&req); err != nil { SendResponse(ctx, http.StatusBadRequest, nil, "", err) return @@ -166,7 +167,7 @@ func CreateFirstAdmin(ctx *gin.Context) { return } - req.Type = "admin" + req.Type = "superadmin" // Validate the requestBody using the validator if err := validate.Struct(req); err != nil { SendResponse(ctx, http.StatusBadRequest, nil, "", err) @@ -236,3 +237,37 @@ func GetAllUsers(ctx *gin.Context) { } SendResponse(ctx, http.StatusOK, users, "fetched users", nil) } + +func CreateCLIUser(ctx *gin.Context) { + var req dto.CreateCLIUser + if err := ctx.ShouldBindJSON(&req); err != nil { + SendResponse(ctx, http.StatusBadRequest, nil, "", err) + return + } + caller, err := utils.FetchUserIDFromCtx(ctx) + if err != nil { + SendResponse(ctx, http.StatusBadRequest, nil, "", errors.New("invalid user id")) + return + } + user, err := service.CreateCLIUser(ctx, req, caller) + if err != nil { + SendResponse(ctx, http.StatusInternalServerError, nil, "", err) + return + } + SendResponse(ctx, http.StatusCreated, user, "created user", nil) +} + +func GetCliUsers(ctx *gin.Context) { + caller, err := utils.FetchUserIDFromCtx(ctx) + if err != nil { + SendResponse(ctx, http.StatusBadRequest, nil, "", errors.New("invalid user id")) + return + } + cliUsers, err := service.GetCliUsers(ctx, caller) + + if err != nil { + SendResponse(ctx, http.StatusInternalServerError, nil, "", err) + return + } + SendResponse(ctx, http.StatusCreated, cliUsers, "fetched cli users user", nil) +} diff --git a/customerrors/errors.go b/customerrors/errors.go index 9e7964a..b3915e6 100644 --- a/customerrors/errors.go +++ b/customerrors/errors.go @@ -68,3 +68,12 @@ type UserNotAdminOfGroupError struct { func (e *UserNotAdminOfGroupError) Error() string { return fmt.Sprintf("user %s is not a admin of group %s", e.UserID, e.GroupID) } + +type UserDoesNotHaveEnvironmentAccess struct { + UserID uuid.UUID + EnvironmentID uuid.UUID +} + +func (e *UserDoesNotHaveEnvironmentAccess) Error() string { + return fmt.Sprintf("user %s does not have access to env %s", e.UserID, e.EnvironmentID) +} diff --git a/db/migration/000002_split_field_table.down.sql b/db/migration/000002_split_field_table.down.sql new file mode 100644 index 0000000..9526317 --- /dev/null +++ b/db/migration/000002_split_field_table.down.sql @@ -0,0 +1,5 @@ +DROP TABLE IF EXISTS field_values; + +DROP TABLE IF EXISTS field_data; + +ALTER TABLE folders DROP COLUMN IF EXISTS type ; diff --git a/db/migration/000002_split_field_table.up.sql b/db/migration/000002_split_field_table.up.sql new file mode 100644 index 0000000..af38ed6 --- /dev/null +++ b/db/migration/000002_split_field_table.up.sql @@ -0,0 +1,19 @@ +CREATE TABLE field_data ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + field_name VARCHAR(255) NOT NULL, + field_type VARCHAR(255) NOT NULL, + credential_id UUID NOT NULL REFERENCES credentials(id) ON DELETE CASCADE, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + created_by UUID REFERENCES users(id) ON DELETE SET NULL, + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + updated_by UUID REFERENCES users(id) ON DELETE SET NULL +); + +CREATE TABLE field_values ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + field_id UUID NOT NULL REFERENCES field_data(id) ON DELETE CASCADE, + field_value TEXT NOT NULL, + user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE +); + +ALTER TABLE folders ADD COLUMN type VARCHAR(255) NOT NULL DEFAULT 'shared'; \ No newline at end of file diff --git a/db/migration/000003_cli_user.down.sql b/db/migration/000003_cli_user.down.sql new file mode 100644 index 0000000..684ed2b --- /dev/null +++ b/db/migration/000003_cli_user.down.sql @@ -0,0 +1,5 @@ +DROP TABLE IF EXISTS environment_fields; + +DROP TABLE IF EXISTS environments; + +ALTER TABLE users DROP COLUMN IF EXISTS created_by; \ No newline at end of file diff --git a/db/migration/000003_cli_user.up.sql b/db/migration/000003_cli_user.up.sql new file mode 100644 index 0000000..6f04f41 --- /dev/null +++ b/db/migration/000003_cli_user.up.sql @@ -0,0 +1,22 @@ + +ALTER TABLE users ADD COLUMN created_by UUID; + +CREATE TABLE environments ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + cli_user UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, + name VARCHAR(255) NOT NULL, + createdAt TIMESTAMPTZ NOT NULL DEFAULT NOW(), + updatedAt TIMESTAMPTZ NOT NULL DEFAULT NOW(), + created_by UUID NOT NULL REFERENCES users(id) +); + +CREATE TABLE environment_fields ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + field_name VARCHAR(255) NOT NULL, + field_value TEXT NOT NULL, + parent_field_value_id UUID NOT NULL REFERENCES field_values(id) ON DELETE CASCADE, + env_id UUID NOT NULL REFERENCES environments(Id) ON DELETE CASCADE, + credential_id UUID NOT NULL REFERENCES credentials(id) ON DELETE CASCADE, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() +); \ No newline at end of file diff --git a/db/query/credential.sql b/db/query/credential.sql index c6a05fc..8606aa3 100644 --- a/db/query/credential.sql +++ b/db/query/credential.sql @@ -42,15 +42,16 @@ WHERE AND C.folder_id = $1 AND A.user_id = $2; - -- name: GetAllUrlsForUser :many SELECT DISTINCT - field_value as value, credential_id as "credentialId" + fv.field_value AS value, + fd.credential_id AS "credentialId" FROM - fields + field_values fv +JOIN + field_data fd ON fv.field_id = fd.id WHERE - user_id = $1 AND field_name = 'Domain'; - + fv.user_id = $1 AND fd.field_name = 'Domain'; -- name: GetCredentialDetailsByIDs :many SELECT @@ -103,17 +104,19 @@ SET description = $3, credential_type = $4, updated_at = NOW(), - updated_by = $5 + updated_by = $5, + domain = $6 WHERE id = $1; -- name: GetCredentialsForSearchByUserID :many -SELECT +SELECT DISTINCT c.id as "credentialId", c.name, COALESCE(c.description, '') AS description, COALESCE(c.domain, '') AS domain, - c.folder_id, + c.folder_id, + COALESCE(f.type, '' ) AS "folderType", COALESCE(f.name, '') AS folder_name FROM credentials c diff --git a/db/query/credential_access.sql b/db/query/credential_access.sql index 6072eb7..a8b9dd6 100644 --- a/db/query/credential_access.sql +++ b/db/query/credential_access.sql @@ -44,6 +44,12 @@ SELECT EXISTS ( AND ((folder_id IS NOT NULL AND folder_id = $4) OR (folder_id is null and $4 is null)) ); +-- name: CheckAnyCredentialAccessEntryExists :one +SELECT EXISTS ( + SELECT 1 + FROM credential_access + WHERE user_id = $1 AND credential_id = $2 +); -- name: RemoveCredentialAccessForUsers :exec DELETE FROM credential_access WHERE group_id IS NULL AND folder_id IS NULL diff --git a/db/query/environment.sql b/db/query/environment.sql new file mode 100644 index 0000000..e98ebf5 --- /dev/null +++ b/db/query/environment.sql @@ -0,0 +1,118 @@ +-- name: AddEnvironment :one +INSERT INTO environments ( + cli_user, + name, + created_by +) VALUES ( + $1, + $2, + $3 +) +RETURNING id; + + +-- name: CheckCredentialExistsForEnv :one +SELECT EXISTS ( + SELECT 1 + FROM environment_fields + WHERE credential_id = $1 AND env_id = $2 +); + + +-- name: CreateEnvFields :one +INSERT INTO environment_fields ( + credential_id, + field_value, + field_name, + parent_field_value_id, + env_id +) VALUES ( + $1, + $2, + $3, + $4, + $5 +) +RETURNING id; + +-- name: GetEnvironmentsForUser :many +SELECT e.*, COALESCE( u.encryption_key, '') as "publicKey", u.username as "cliUsername" +FROM environments e +JOIN users u ON e.cli_user = u.id +WHERE e.cli_user IN ( + SELECT id + FROM users + WHERE u.created_by = $1 AND type = 'cli' +); + +-- name: GetEnvironmentByID :one +SELECT * from environments WHERE id = $1 and created_by = $2; + +-- name: GetEnvFields :many +SELECT + fv.field_value, + ef.field_name, + ef.id, + ef.credential_id, + c.name as "credentialName" +FROM environment_fields ef +JOIN field_values fv ON ef.parent_field_value_id = fv.id +JOIN credentials c ON ef.credential_id = c.id +WHERE ef.env_id = $1; + + +-- name: GetEnvironmentFieldsByName :many +SELECT ef.id, ef.field_name, ef.field_value, ef.credential_id +FROM environment_fields ef +JOIN environments e ON ef.env_id = e.Id +WHERE e.name = $1; + + +-- name: EditEnvironmentFieldNameByID :one +UPDATE environment_fields +SET field_name = $1, updated_at = NOW() +WHERE id = $2 and env_id = $3 +RETURNING field_name; + + +-- name: IsEnvironmentOwner :one +SELECT EXISTS ( + SELECT 1 + FROM environments + WHERE id = $1 AND created_by = $2 +); + + +-- name: GetUserEnvsForCredential :many +SELECT e.id +FROM environments e +JOIN environment_fields ef ON e.id = ef.env_id +WHERE ef.credential_id = $1 AND cli_user = $2; + +-- name: EditEnvFieldValue :exec +UPDATE environment_fields +SET field_value = $1, updated_at = NOW() +WHERE id = $2; + + +-- name: GetEnvFieldsForCredential :many +SELECT ef.id as envFieldID, ef.env_id, fd.id as fieldID, u.id as userID, u.encryption_key as "publicKey" +FROM environment_fields as ef + JOIN environments e ON ef.env_id = e.id + JOIN field_values fv ON ef.parent_field_value_id = fv.id + JOIN field_data fd ON fv.field_id = fd.id + JOIN users u ON e.cli_user = u.id +WHERE ef.credential_id = $1; + + +-- name: GetEnvForCredential :many +SELECT + e.id as "envId", + COALESCE(u.encryption_key, '') as "cliUserPublicKey", + e.cli_user as "cliUserId", + u.created_by as "cliUserCreatedBy" +FROM environments e +JOIN environment_fields ef ON e.id = ef.env_id +JOIN users u ON e.cli_user = u.id +WHERE ef.credential_id = $1 +GROUP BY e.id, u.encryption_key, e.cli_user, u.created_by; \ No newline at end of file diff --git a/db/query/field.sql b/db/query/field.sql index 4480ebe..58da8cf 100644 --- a/db/query/field.sql +++ b/db/query/field.sql @@ -13,29 +13,32 @@ WHERE credential_id = $1; -- name: GetNonSensitiveFieldsForCredentialIDs :many SELECT - f.id, - f.credential_id, - f.field_name, - f.field_value, - f.field_type -FROM fields as f + fd.id, + fd.credential_id, + fd.field_name, + fv.field_value, + fd.field_type +FROM field_data as fd +JOIN field_values as fv ON fd.id = fv.field_id WHERE -field_type != 'sensitive' -AND f.user_id = $1 -AND f.credential_id = ANY(@credentials::UUID[]); +(field_type = 'meta' OR field_type = 'additional') +AND fv.user_id = $1 +AND fd.field_type != 'totp' +AND fd.credential_id = ANY(@credentialids::UUID[]); -- name: GetAllFieldsForCredentialIDs :many SELECT - f.id, - f.credential_id, - f.field_name, - f.field_value, - f.field_type -FROM fields as f + fd.id, + fd.field_name, + fv.field_value, + fd.field_type, + fd.credential_id +FROM field_data as fd +JOIN field_values as fv ON fd.id = fv.field_id WHERE -f.user_id = $1 -AND f.credential_id = ANY(@credentials::UUID[]); +fv.user_id = $1 +AND fd.credential_id = ANY(@credentials::UUID[]); -- name: CheckFieldEntryExists :one @@ -47,14 +50,16 @@ SELECT EXISTS ( -- name: GetSensitiveFields :many SELECT - f.id, - f.field_name, - f.field_value -FROM fields as f + fd.id, + fd.field_name, + fv.field_value, + fd.field_type +FROM field_data as fd +JOIN field_values as fv ON fd.id = fv.field_id WHERE -field_type = 'sensitive' -AND f.credential_id = $1 -AND f.user_id = $2; +(fd.field_type = 'sensitive' OR fd.field_type = 'totp') +AND fd.credential_id = $1 +AND fv.user_id = $2; -- name: RemoveCredentialFieldsForUsers :exec @@ -62,24 +67,32 @@ DELETE FROM fields WHERE credential_id = $1 AND user_id = ANY(@user_ids::UUID[]) -- name: DeleteAccessRemovedFields :exec -DELETE FROM fields -WHERE - EXISTS ( - -- Select fields rows that don't have a corresponding entry in credential_access - SELECT 1 - FROM fields f - WHERE - NOT EXISTS ( - -- Look for a matching entry in credential_access - SELECT 1 - FROM credential_access ca - WHERE - ca.credential_id = f.credential_id - AND ca.user_id = f.user_id - ) - AND f.credential_id = fields.credential_id - AND f.user_id = fields.user_id - ); +DELETE FROM field_values fv +WHERE EXISTS ( + -- Select fields_values rows that don't have a corresponding entry in credential_access + SELECT 1 + FROM field_data fd + WHERE + fd.id = fv.field_id + AND NOT EXISTS ( + -- Look for a matching entry in credential_access + SELECT 1 + FROM credential_access ca + WHERE + ca.credential_id = fd.credential_id + AND ca.user_id = fv.user_id + ) +); + + +-- name: EditFieldData :exec +UPDATE field_data +SET field_name = $1, field_type = $2, updated_by = $3, updated_at = NOW() +WHERE id = $4; +-- name: EditFieldValue :exec +UPDATE field_values +SET field_value = $1 +WHERE field_id = $2 AND user_id = $3; diff --git a/db/query/field_data.sql b/db/query/field_data.sql new file mode 100644 index 0000000..93e99aa --- /dev/null +++ b/db/query/field_data.sql @@ -0,0 +1,7 @@ +-- name: AddFieldData :one +INSERT INTO field_data (field_name, field_type, credential_id, created_by, updated_by) +VALUES ($1, $2, $3, $4, $5) +RETURNING id; + +-- name: DeleteFields :exec +DELETE FROM field_data WHERE id = ANY(@field_ids::UUID[]); \ No newline at end of file diff --git a/db/query/field_values.sql b/db/query/field_values.sql new file mode 100644 index 0000000..0fd9ed7 --- /dev/null +++ b/db/query/field_values.sql @@ -0,0 +1,7 @@ + +-- name: AddFieldValue :one +INSERT INTO field_values (field_id, field_value, user_id) +VALUES ($1, $2, $3) RETURNING id; + +-- name: GetFieldValueIDsForFieldIDs :many +SELECT id, field_id FROM field_values WHERE field_id = ANY(@fieldIDs::UUID[]) and user_id = $1; \ No newline at end of file diff --git a/db/query/folder.sql b/db/query/folder.sql index f426dea..0d6f6f3 100644 --- a/db/query/folder.sql +++ b/db/query/folder.sql @@ -1,12 +1,12 @@ -- name: AddFolder :one -INSERT INTO folders (name, description, created_by) -VALUES ($1, $2, $3) +INSERT INTO folders (name, description, created_by, type) +VALUES ($1, $2, $3, $4) RETURNING id, created_at; -- name: FetchAccessibleFoldersForUser :many -SELECT folders.id, folders.name, folders.description, folders.created_at, folders.created_by, COALESCE(folder_access.access_type, 'none') as "accessType" +SELECT folders.id, folders.name, folders.created_at, folders.created_by, COALESCE(folder_access.access_type, 'none') as "accessType", folders.type, COALESCE(folders.description, '') as "description" FROM folders LEFT JOIN folder_access ON folders.id = folder_access.folder_id AND folder_access.user_id = $1 WHERE folders.id IN ( diff --git a/db/query/groups.sql b/db/query/groups.sql index 77e6b34..4ca6adf 100644 --- a/db/query/groups.sql +++ b/db/query/groups.sql @@ -88,7 +88,7 @@ WHERE g.id NOT IN ( SELECT u.id, u.username, u.name, COALESCE(u.encryption_key,'') as "publicKey" FROM users u LEFT JOIN group_list gl ON (u.id = gl.user_id AND gl.grouping_id = $1) -WHERE gl.grouping_id IS NULL and u.status = 'active'; +WHERE gl.grouping_id IS NULL and u.status = 'active' and u.type != 'cli'; -- name: RemoveUserFromGroupList :exec DELETE FROM group_list diff --git a/db/query/user.sql b/db/query/user.sql index b219692..0e5dec7 100644 --- a/db/query/user.sql +++ b/db/query/user.sql @@ -21,7 +21,7 @@ WHERE username = $2; -- name: GetAllSignedUpUsers :many -SELECT id,name,username, COALESCE(encryption_key, '') AS "publicKey" FROM users where signed_up = true; +SELECT id,name,username, COALESCE(encryption_key, '') AS "publicKey" FROM users where signed_up = true and type !='cli'; -- name: GetUserByPublicKey :one @@ -81,4 +81,43 @@ SELECT id,name,username, status, type FROM users ; -- name: GetUserDeviceKey :one -SELECT COALESCE(device_key,'') as "deviceKey" FROM users WHERE id = $1; \ No newline at end of file +SELECT COALESCE(device_key,'') as "deviceKey" FROM users WHERE id = $1; + + +-- name: CreateCliUser :one +INSERT INTO users ( + + username, + name, + encryption_key, + device_key, + temp_password, + registration_challenge, + signed_up, + type, + status, + created_by +) VALUES ( + $1, + $2, + $3, + $4, + $5, + $6, + $7, + $8, + $9, + $10 +) +RETURNING id; + + +-- name: GetCliUsers :many +SELECT id, username FROM users WHERE type = 'cli' and created_by = $1; + + +-- name: GetSuperUser :one +select * from users where type = 'superadmin' limit 1; + +-- name: CheckCliUser :one +SELECT EXISTS(SELECT 1 FROM users WHERE id = $1 AND type = 'cli'); \ No newline at end of file diff --git a/db/sqlc/credential.sql.go b/db/sqlc/credential.sql.go index 9ffed0e..7e0e3d6 100644 --- a/db/sqlc/credential.sql.go +++ b/db/sqlc/credential.sql.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.24.0 +// sqlc v1.26.0 // source: credential.sql package db @@ -53,7 +53,8 @@ SET description = $3, credential_type = $4, updated_at = NOW(), - updated_by = $5 + updated_by = $5, + domain = $6 WHERE id = $1 ` @@ -64,6 +65,7 @@ type EditCredentialDetailsParams struct { Description sql.NullString `json:"description"` CredentialType string `json:"credentialType"` UpdatedBy uuid.NullUUID `json:"updatedBy"` + Domain sql.NullString `json:"domain"` } func (q *Queries) EditCredentialDetails(ctx context.Context, arg EditCredentialDetailsParams) error { @@ -73,6 +75,7 @@ func (q *Queries) EditCredentialDetails(ctx context.Context, arg EditCredentialD arg.Description, arg.CredentialType, arg.UpdatedBy, + arg.Domain, ) return err } @@ -191,11 +194,14 @@ func (q *Queries) GetAccessTypeAndGroupsByCredentialId(ctx context.Context, cred const getAllUrlsForUser = `-- name: GetAllUrlsForUser :many SELECT DISTINCT - field_value as value, credential_id as "credentialId" + fv.field_value AS value, + fd.credential_id AS "credentialId" FROM - fields + field_values fv +JOIN + field_data fd ON fv.field_id = fd.id WHERE - user_id = $1 AND field_name = 'Domain' + fv.user_id = $1 AND fd.field_name = 'Domain' ` type GetAllUrlsForUserRow struct { @@ -375,12 +381,13 @@ func (q *Queries) GetCredentialIdsByFolder(ctx context.Context, arg GetCredentia } const getCredentialsForSearchByUserID = `-- name: GetCredentialsForSearchByUserID :many -SELECT +SELECT DISTINCT c.id as "credentialId", c.name, COALESCE(c.description, '') AS description, COALESCE(c.domain, '') AS domain, - c.folder_id, + c.folder_id, + COALESCE(f.type, '' ) AS "folderType", COALESCE(f.name, '') AS folder_name FROM credentials c @@ -398,6 +405,7 @@ type GetCredentialsForSearchByUserIDRow struct { Description string `json:"description"` Domain string `json:"domain"` FolderID uuid.UUID `json:"folderId"` + FolderType string `json:"folderType"` FolderName string `json:"folderName"` } @@ -416,6 +424,7 @@ func (q *Queries) GetCredentialsForSearchByUserID(ctx context.Context, userID uu &i.Description, &i.Domain, &i.FolderID, + &i.FolderType, &i.FolderName, ); err != nil { return nil, err diff --git a/db/sqlc/credential_access.sql.go b/db/sqlc/credential_access.sql.go index 913da0c..7f9d8f4 100644 --- a/db/sqlc/credential_access.sql.go +++ b/db/sqlc/credential_access.sql.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.24.0 +// sqlc v1.26.0 // source: credential_access.sql package db @@ -39,6 +39,26 @@ func (q *Queries) AddCredentialAccess(ctx context.Context, arg AddCredentialAcce return id, err } +const checkAnyCredentialAccessEntryExists = `-- name: CheckAnyCredentialAccessEntryExists :one +SELECT EXISTS ( + SELECT 1 + FROM credential_access + WHERE user_id = $1 AND credential_id = $2 +) +` + +type CheckAnyCredentialAccessEntryExistsParams struct { + UserID uuid.UUID `json:"userId"` + CredentialID uuid.UUID `json:"credentialId"` +} + +func (q *Queries) CheckAnyCredentialAccessEntryExists(ctx context.Context, arg CheckAnyCredentialAccessEntryExistsParams) (bool, error) { + row := q.db.QueryRowContext(ctx, checkAnyCredentialAccessEntryExists, arg.UserID, arg.CredentialID) + var exists bool + err := row.Scan(&exists) + return exists, err +} + const checkCredentialAccessEntryExists = `-- name: CheckCredentialAccessEntryExists :one SELECT EXISTS ( SELECT 1 diff --git a/db/sqlc/credential_transactions.go b/db/sqlc/credential_transactions.go index 8ddc93b..84b871f 100644 --- a/db/sqlc/credential_transactions.go +++ b/db/sqlc/credential_transactions.go @@ -14,7 +14,7 @@ type AddCredentialTransactionParams struct { FolderID uuid.UUID CredentialType string CreatedBy uuid.UUID - UserFields []dto.UserFields + Fields []dto.Fields CredentialAccessArgs []AddCredentialAccessParams Domain string } @@ -41,15 +41,23 @@ func (store *SQLStore) AddCredentialTransaction(ctx context.Context, args AddCre } // Create field records - for _, userFields := range args.UserFields { - for _, field := range userFields.Fields { - _, err = q.AddField(ctx, AddFieldParams{ - FieldName: field.FieldName, - FieldValue: field.FieldValue, - FieldType: field.FieldType, - CredentialID: credentialID, - UserID: userFields.UserID, - CreatedBy: uuid.NullUUID{UUID: args.CreatedBy, Valid: true}, + for _, field := range args.Fields { + + fieldID, err := q.AddFieldData(ctx, AddFieldDataParams{ + FieldName: field.FieldName, + FieldType: field.FieldType, + CredentialID: credentialID, + CreatedBy: uuid.NullUUID{UUID: args.CreatedBy, Valid: true}, + }) + if err != nil { + return err + } + + for _, userField := range field.FieldValues { + _, err := q.AddFieldValue(ctx, AddFieldValueParams{ + FieldID: fieldID, + FieldValue: userField.FieldValue, + UserID: userField.UserID, }) if err != nil { return err @@ -72,16 +80,7 @@ func (store *SQLStore) AddCredentialTransaction(ctx context.Context, args AddCre return credentialID, err } -type EditCredentialTransactionParams struct { - CredentialID uuid.UUID - Name string - Description sql.NullString - CredentialType string - UserFields []dto.UserFields - EditedBy uuid.UUID -} - -func (store *SQLStore) EditCredentialTransaction(ctx context.Context, args EditCredentialTransactionParams) error { +func (store *SQLStore) EditCredentialTransaction(ctx context.Context, args dto.EditCredentialRequest, editedBy uuid.UUID) error { err := store.execTx(ctx, func(q *Queries) error { @@ -90,36 +89,96 @@ func (store *SQLStore) EditCredentialTransaction(ctx context.Context, args EditC editCredentialDetailsParams := EditCredentialDetailsParams{ ID: args.CredentialID, Name: args.Name, - Description: args.Description, + Description: sql.NullString{String: args.Description, Valid: true}, CredentialType: args.CredentialType, - UpdatedBy: uuid.NullUUID{UUID: args.EditedBy, Valid: true}, + UpdatedBy: uuid.NullUUID{UUID: editedBy, Valid: true}, + Domain: sql.NullString{String: args.Domain, Valid: true}, } err = q.EditCredentialDetails(ctx, editCredentialDetailsParams) if err != nil { return err } - // Delete existing field records - err = q.DeleteCredentialFields(ctx, args.CredentialID) - if err != nil { - return err + // Edit User Fields + for _, field := range args.EditedUserFields { + + // Edit field data + err = q.EditFieldData(ctx, EditFieldDataParams{ + ID: field.FieldID, + FieldName: field.FieldName, + FieldType: field.FieldType, + UpdatedBy: uuid.NullUUID{UUID: editedBy, Valid: true}, + }) + if err != nil { + return err + } + + for _, userValue := range field.FieldValues { + err = q.EditFieldValue(ctx, EditFieldValueParams{ + FieldValue: userValue.FieldValue, + UserID: userValue.UserID, + FieldID: field.FieldID, + }) + if err != nil { + return err + } + } } - // Create field records - for _, userFields := range args.UserFields { - for _, field := range userFields.Fields { - _, err = q.AddField(ctx, AddFieldParams{ - FieldName: field.FieldName, - FieldValue: field.FieldValue, - CredentialID: args.CredentialID, - UserID: userFields.UserID, - FieldType: field.FieldType, - CreatedBy: uuid.NullUUID{UUID: args.EditedBy, Valid: true}, + // Edit Env Fields + for _, field := range args.EditedEnvFields { + err = q.EditEnvFieldValue(ctx, EditEnvFieldValueParams{ + FieldValue: field.FieldValue, + ID: field.EnvFieldID, + }) + if err != nil { + return err + } + } + + // Add new fields + for _, field := range args.NewFields { + + // Add field data + fieldID, err := q.AddFieldData(ctx, AddFieldDataParams{ + FieldName: field.FieldName, + FieldType: field.FieldType, + CredentialID: args.CredentialID, + CreatedBy: uuid.NullUUID{UUID: editedBy, Valid: true}, + }) + if err != nil { + return err + } + + for _, userField := range field.FieldValues { + fieldValueID, err := q.AddFieldValue(ctx, AddFieldValueParams{ + FieldID: fieldID, + FieldValue: userField.FieldValue, + UserID: userField.UserID, }) if err != nil { return err } + + for _, envField := range userField.EnvFieldValues { + _, err = q.CreateEnvFields(ctx, CreateEnvFieldsParams{ + CredentialID: args.CredentialID, + FieldValue: envField.FieldValue, + FieldName: field.FieldName, + ParentFieldValueID: fieldValueID, + EnvID: envField.EnvID, + }) + if err != nil { + return err + } + } } + + } + + err = q.DeleteFields(ctx, args.DeletedFields) + if err != nil { + return err } return nil diff --git a/db/sqlc/db.go b/db/sqlc/db.go index e06d75a..17d86e9 100644 --- a/db/sqlc/db.go +++ b/db/sqlc/db.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.24.0 +// sqlc v1.26.0 package db diff --git a/db/sqlc/environment.sql.go b/db/sqlc/environment.sql.go new file mode 100644 index 0000000..2086104 --- /dev/null +++ b/db/sqlc/environment.sql.go @@ -0,0 +1,453 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.26.0 +// source: environment.sql + +package db + +import ( + "context" + "database/sql" + "time" + + "github.com/google/uuid" +) + +const addEnvironment = `-- name: AddEnvironment :one +INSERT INTO environments ( + cli_user, + name, + created_by +) VALUES ( + $1, + $2, + $3 +) +RETURNING id +` + +type AddEnvironmentParams struct { + CliUser uuid.UUID `json:"cliUser"` + Name string `json:"name"` + CreatedBy uuid.UUID `json:"createdBy"` +} + +func (q *Queries) AddEnvironment(ctx context.Context, arg AddEnvironmentParams) (uuid.UUID, error) { + row := q.db.QueryRowContext(ctx, addEnvironment, arg.CliUser, arg.Name, arg.CreatedBy) + var id uuid.UUID + err := row.Scan(&id) + return id, err +} + +const checkCredentialExistsForEnv = `-- name: CheckCredentialExistsForEnv :one +SELECT EXISTS ( + SELECT 1 + FROM environment_fields + WHERE credential_id = $1 AND env_id = $2 +) +` + +type CheckCredentialExistsForEnvParams struct { + CredentialID uuid.UUID `json:"credentialId"` + EnvID uuid.UUID `json:"envId"` +} + +func (q *Queries) CheckCredentialExistsForEnv(ctx context.Context, arg CheckCredentialExistsForEnvParams) (bool, error) { + row := q.db.QueryRowContext(ctx, checkCredentialExistsForEnv, arg.CredentialID, arg.EnvID) + var exists bool + err := row.Scan(&exists) + return exists, err +} + +const createEnvFields = `-- name: CreateEnvFields :one +INSERT INTO environment_fields ( + credential_id, + field_value, + field_name, + parent_field_value_id, + env_id +) VALUES ( + $1, + $2, + $3, + $4, + $5 +) +RETURNING id +` + +type CreateEnvFieldsParams struct { + CredentialID uuid.UUID `json:"credentialId"` + FieldValue string `json:"fieldValue"` + FieldName string `json:"fieldName"` + ParentFieldValueID uuid.UUID `json:"parentFieldValueId"` + EnvID uuid.UUID `json:"envId"` +} + +func (q *Queries) CreateEnvFields(ctx context.Context, arg CreateEnvFieldsParams) (uuid.UUID, error) { + row := q.db.QueryRowContext(ctx, createEnvFields, + arg.CredentialID, + arg.FieldValue, + arg.FieldName, + arg.ParentFieldValueID, + arg.EnvID, + ) + var id uuid.UUID + err := row.Scan(&id) + return id, err +} + +const editEnvFieldValue = `-- name: EditEnvFieldValue :exec +UPDATE environment_fields +SET field_value = $1, updated_at = NOW() +WHERE id = $2 +` + +type EditEnvFieldValueParams struct { + FieldValue string `json:"fieldValue"` + ID uuid.UUID `json:"id"` +} + +func (q *Queries) EditEnvFieldValue(ctx context.Context, arg EditEnvFieldValueParams) error { + _, err := q.db.ExecContext(ctx, editEnvFieldValue, arg.FieldValue, arg.ID) + return err +} + +const editEnvironmentFieldNameByID = `-- name: EditEnvironmentFieldNameByID :one +UPDATE environment_fields +SET field_name = $1, updated_at = NOW() +WHERE id = $2 and env_id = $3 +RETURNING field_name +` + +type EditEnvironmentFieldNameByIDParams struct { + FieldName string `json:"fieldName"` + ID uuid.UUID `json:"id"` + EnvID uuid.UUID `json:"envId"` +} + +func (q *Queries) EditEnvironmentFieldNameByID(ctx context.Context, arg EditEnvironmentFieldNameByIDParams) (string, error) { + row := q.db.QueryRowContext(ctx, editEnvironmentFieldNameByID, arg.FieldName, arg.ID, arg.EnvID) + var field_name string + err := row.Scan(&field_name) + return field_name, err +} + +const getEnvFields = `-- name: GetEnvFields :many +SELECT + fv.field_value, + ef.field_name, + ef.id, + ef.credential_id, + c.name as "credentialName" +FROM environment_fields ef +JOIN field_values fv ON ef.parent_field_value_id = fv.id +JOIN credentials c ON ef.credential_id = c.id +WHERE ef.env_id = $1 +` + +type GetEnvFieldsRow struct { + FieldValue string `json:"fieldValue"` + FieldName string `json:"fieldName"` + ID uuid.UUID `json:"id"` + CredentialID uuid.UUID `json:"credentialId"` + CredentialName string `json:"credentialName"` +} + +func (q *Queries) GetEnvFields(ctx context.Context, envID uuid.UUID) ([]GetEnvFieldsRow, error) { + rows, err := q.db.QueryContext(ctx, getEnvFields, envID) + if err != nil { + return nil, err + } + defer rows.Close() + items := []GetEnvFieldsRow{} + for rows.Next() { + var i GetEnvFieldsRow + if err := rows.Scan( + &i.FieldValue, + &i.FieldName, + &i.ID, + &i.CredentialID, + &i.CredentialName, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const getEnvFieldsForCredential = `-- name: GetEnvFieldsForCredential :many +SELECT ef.id as envFieldID, ef.env_id, fd.id as fieldID, u.id as userID, u.encryption_key as "publicKey" +FROM environment_fields as ef + JOIN environments e ON ef.env_id = e.id + JOIN field_values fv ON ef.parent_field_value_id = fv.id + JOIN field_data fd ON fv.field_id = fd.id + JOIN users u ON e.cli_user = u.id +WHERE ef.credential_id = $1 +` + +type GetEnvFieldsForCredentialRow struct { + Envfieldid uuid.UUID `json:"envfieldid"` + EnvID uuid.UUID `json:"envId"` + Fieldid uuid.UUID `json:"fieldid"` + Userid uuid.UUID `json:"userid"` + PublicKey sql.NullString `json:"publicKey"` +} + +func (q *Queries) GetEnvFieldsForCredential(ctx context.Context, credentialID uuid.UUID) ([]GetEnvFieldsForCredentialRow, error) { + rows, err := q.db.QueryContext(ctx, getEnvFieldsForCredential, credentialID) + if err != nil { + return nil, err + } + defer rows.Close() + items := []GetEnvFieldsForCredentialRow{} + for rows.Next() { + var i GetEnvFieldsForCredentialRow + if err := rows.Scan( + &i.Envfieldid, + &i.EnvID, + &i.Fieldid, + &i.Userid, + &i.PublicKey, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const getEnvForCredential = `-- name: GetEnvForCredential :many +SELECT + e.id as "envId", + COALESCE(u.encryption_key, '') as "cliUserPublicKey", + e.cli_user as "cliUserId", + u.created_by as "cliUserCreatedBy" +FROM environments e +JOIN environment_fields ef ON e.id = ef.env_id +JOIN users u ON e.cli_user = u.id +WHERE ef.credential_id = $1 +GROUP BY e.id, u.encryption_key, e.cli_user, u.created_by +` + +type GetEnvForCredentialRow struct { + EnvId uuid.UUID `json:"envId"` + CliUserPublicKey string `json:"cliUserPublicKey"` + CliUserId uuid.UUID `json:"cliUserId"` + CliUserCreatedBy uuid.NullUUID `json:"cliUserCreatedBy"` +} + +func (q *Queries) GetEnvForCredential(ctx context.Context, credentialID uuid.UUID) ([]GetEnvForCredentialRow, error) { + rows, err := q.db.QueryContext(ctx, getEnvForCredential, credentialID) + if err != nil { + return nil, err + } + defer rows.Close() + items := []GetEnvForCredentialRow{} + for rows.Next() { + var i GetEnvForCredentialRow + if err := rows.Scan( + &i.EnvId, + &i.CliUserPublicKey, + &i.CliUserId, + &i.CliUserCreatedBy, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const getEnvironmentByID = `-- name: GetEnvironmentByID :one +SELECT id, cli_user, name, createdat, updatedat, created_by from environments WHERE id = $1 and created_by = $2 +` + +type GetEnvironmentByIDParams struct { + ID uuid.UUID `json:"id"` + CreatedBy uuid.UUID `json:"createdBy"` +} + +func (q *Queries) GetEnvironmentByID(ctx context.Context, arg GetEnvironmentByIDParams) (Environment, error) { + row := q.db.QueryRowContext(ctx, getEnvironmentByID, arg.ID, arg.CreatedBy) + var i Environment + err := row.Scan( + &i.ID, + &i.CliUser, + &i.Name, + &i.Createdat, + &i.Updatedat, + &i.CreatedBy, + ) + return i, err +} + +const getEnvironmentFieldsByName = `-- name: GetEnvironmentFieldsByName :many +SELECT ef.id, ef.field_name, ef.field_value, ef.credential_id +FROM environment_fields ef +JOIN environments e ON ef.env_id = e.Id +WHERE e.name = $1 +` + +type GetEnvironmentFieldsByNameRow struct { + ID uuid.UUID `json:"id"` + FieldName string `json:"fieldName"` + FieldValue string `json:"fieldValue"` + CredentialID uuid.UUID `json:"credentialId"` +} + +func (q *Queries) GetEnvironmentFieldsByName(ctx context.Context, name string) ([]GetEnvironmentFieldsByNameRow, error) { + rows, err := q.db.QueryContext(ctx, getEnvironmentFieldsByName, name) + if err != nil { + return nil, err + } + defer rows.Close() + items := []GetEnvironmentFieldsByNameRow{} + for rows.Next() { + var i GetEnvironmentFieldsByNameRow + if err := rows.Scan( + &i.ID, + &i.FieldName, + &i.FieldValue, + &i.CredentialID, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const getEnvironmentsForUser = `-- name: GetEnvironmentsForUser :many +SELECT e.id, e.cli_user, e.name, e.createdat, e.updatedat, e.created_by, COALESCE( u.encryption_key, '') as "publicKey", u.username as "cliUsername" +FROM environments e +JOIN users u ON e.cli_user = u.id +WHERE e.cli_user IN ( + SELECT id + FROM users + WHERE u.created_by = $1 AND type = 'cli' +) +` + +type GetEnvironmentsForUserRow struct { + ID uuid.UUID `json:"id"` + CliUser uuid.UUID `json:"cliUser"` + Name string `json:"name"` + Createdat time.Time `json:"createdat"` + Updatedat time.Time `json:"updatedat"` + CreatedBy uuid.UUID `json:"createdBy"` + PublicKey string `json:"publicKey"` + CliUsername string `json:"cliUsername"` +} + +func (q *Queries) GetEnvironmentsForUser(ctx context.Context, createdBy uuid.NullUUID) ([]GetEnvironmentsForUserRow, error) { + rows, err := q.db.QueryContext(ctx, getEnvironmentsForUser, createdBy) + if err != nil { + return nil, err + } + defer rows.Close() + items := []GetEnvironmentsForUserRow{} + for rows.Next() { + var i GetEnvironmentsForUserRow + if err := rows.Scan( + &i.ID, + &i.CliUser, + &i.Name, + &i.Createdat, + &i.Updatedat, + &i.CreatedBy, + &i.PublicKey, + &i.CliUsername, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const getUserEnvsForCredential = `-- name: GetUserEnvsForCredential :many +SELECT e.id +FROM environments e +JOIN environment_fields ef ON e.id = ef.env_id +WHERE ef.credential_id = $1 AND cli_user = $2 +` + +type GetUserEnvsForCredentialParams struct { + CredentialID uuid.UUID `json:"credentialId"` + CliUser uuid.UUID `json:"cliUser"` +} + +func (q *Queries) GetUserEnvsForCredential(ctx context.Context, arg GetUserEnvsForCredentialParams) ([]uuid.UUID, error) { + rows, err := q.db.QueryContext(ctx, getUserEnvsForCredential, arg.CredentialID, arg.CliUser) + if err != nil { + return nil, err + } + defer rows.Close() + items := []uuid.UUID{} + for rows.Next() { + var id uuid.UUID + if err := rows.Scan(&id); err != nil { + return nil, err + } + items = append(items, id) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const isEnvironmentOwner = `-- name: IsEnvironmentOwner :one +SELECT EXISTS ( + SELECT 1 + FROM environments + WHERE id = $1 AND created_by = $2 +) +` + +type IsEnvironmentOwnerParams struct { + ID uuid.UUID `json:"id"` + CreatedBy uuid.UUID `json:"createdBy"` +} + +func (q *Queries) IsEnvironmentOwner(ctx context.Context, arg IsEnvironmentOwnerParams) (bool, error) { + row := q.db.QueryRowContext(ctx, isEnvironmentOwner, arg.ID, arg.CreatedBy) + var exists bool + err := row.Scan(&exists) + return exists, err +} diff --git a/db/sqlc/environment_transactions.go b/db/sqlc/environment_transactions.go new file mode 100644 index 0000000..1ac6bec --- /dev/null +++ b/db/sqlc/environment_transactions.go @@ -0,0 +1,27 @@ +package db + +import ( + "context" + dto "osvauld/dtos" +) + +func (store *SQLStore) AddCredentialFieldToEnvTxn(ctx context.Context, args []dto.CredentialEnvData) error { + + err := store.execTx(ctx, func(q *Queries) error { + for _, arg := range args { + _, err := q.CreateEnvFields(ctx, CreateEnvFieldsParams{ + EnvID: arg.EnvID, + ParentFieldValueID: arg.ParentFieldValueID, + FieldValue: arg.FieldValue, + FieldName: arg.FieldName, + CredentialID: arg.CredentialID, + }) + if err != nil { + return err + } + } + return nil + }) + return err + +} diff --git a/db/sqlc/field.sql.go b/db/sqlc/field.sql.go index a1acde1..8d99082 100644 --- a/db/sqlc/field.sql.go +++ b/db/sqlc/field.sql.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.24.0 +// sqlc v1.26.0 // source: field.sql package db @@ -63,24 +63,22 @@ func (q *Queries) CheckFieldEntryExists(ctx context.Context, arg CheckFieldEntry } const deleteAccessRemovedFields = `-- name: DeleteAccessRemovedFields :exec -DELETE FROM fields -WHERE - EXISTS ( - -- Select fields rows that don't have a corresponding entry in credential_access - SELECT 1 - FROM fields f - WHERE - NOT EXISTS ( - -- Look for a matching entry in credential_access - SELECT 1 - FROM credential_access ca - WHERE - ca.credential_id = f.credential_id - AND ca.user_id = f.user_id - ) - AND f.credential_id = fields.credential_id - AND f.user_id = fields.user_id - ) +DELETE FROM field_values fv +WHERE EXISTS ( + -- Select fields_values rows that don't have a corresponding entry in credential_access + SELECT 1 + FROM field_data fd + WHERE + fd.id = fv.field_id + AND NOT EXISTS ( + -- Look for a matching entry in credential_access + SELECT 1 + FROM credential_access ca + WHERE + ca.credential_id = fd.credential_id + AND ca.user_id = fv.user_id + ) +) ` func (q *Queries) DeleteAccessRemovedFields(ctx context.Context) error { @@ -98,17 +96,58 @@ func (q *Queries) DeleteCredentialFields(ctx context.Context, credentialID uuid. return err } +const editFieldData = `-- name: EditFieldData :exec +UPDATE field_data +SET field_name = $1, field_type = $2, updated_by = $3, updated_at = NOW() +WHERE id = $4 +` + +type EditFieldDataParams struct { + FieldName string `json:"fieldName"` + FieldType string `json:"fieldType"` + UpdatedBy uuid.NullUUID `json:"updatedBy"` + ID uuid.UUID `json:"id"` +} + +func (q *Queries) EditFieldData(ctx context.Context, arg EditFieldDataParams) error { + _, err := q.db.ExecContext(ctx, editFieldData, + arg.FieldName, + arg.FieldType, + arg.UpdatedBy, + arg.ID, + ) + return err +} + +const editFieldValue = `-- name: EditFieldValue :exec +UPDATE field_values +SET field_value = $1 +WHERE field_id = $2 AND user_id = $3 +` + +type EditFieldValueParams struct { + FieldValue string `json:"fieldValue"` + FieldID uuid.UUID `json:"fieldId"` + UserID uuid.UUID `json:"userId"` +} + +func (q *Queries) EditFieldValue(ctx context.Context, arg EditFieldValueParams) error { + _, err := q.db.ExecContext(ctx, editFieldValue, arg.FieldValue, arg.FieldID, arg.UserID) + return err +} + const getAllFieldsForCredentialIDs = `-- name: GetAllFieldsForCredentialIDs :many SELECT - f.id, - f.credential_id, - f.field_name, - f.field_value, - f.field_type -FROM fields as f + fd.id, + fd.field_name, + fv.field_value, + fd.field_type, + fd.credential_id +FROM field_data as fd +JOIN field_values as fv ON fd.id = fv.field_id WHERE -f.user_id = $1 -AND f.credential_id = ANY($2::UUID[]) +fv.user_id = $1 +AND fd.credential_id = ANY($2::UUID[]) ` type GetAllFieldsForCredentialIDsParams struct { @@ -118,10 +157,10 @@ type GetAllFieldsForCredentialIDsParams struct { type GetAllFieldsForCredentialIDsRow struct { ID uuid.UUID `json:"id"` - CredentialID uuid.UUID `json:"credentialId"` FieldName string `json:"fieldName"` FieldValue string `json:"fieldValue"` FieldType string `json:"fieldType"` + CredentialID uuid.UUID `json:"credentialId"` } func (q *Queries) GetAllFieldsForCredentialIDs(ctx context.Context, arg GetAllFieldsForCredentialIDsParams) ([]GetAllFieldsForCredentialIDsRow, error) { @@ -135,10 +174,10 @@ func (q *Queries) GetAllFieldsForCredentialIDs(ctx context.Context, arg GetAllFi var i GetAllFieldsForCredentialIDsRow if err := rows.Scan( &i.ID, - &i.CredentialID, &i.FieldName, &i.FieldValue, &i.FieldType, + &i.CredentialID, ); err != nil { return nil, err } @@ -155,21 +194,23 @@ func (q *Queries) GetAllFieldsForCredentialIDs(ctx context.Context, arg GetAllFi const getNonSensitiveFieldsForCredentialIDs = `-- name: GetNonSensitiveFieldsForCredentialIDs :many SELECT - f.id, - f.credential_id, - f.field_name, - f.field_value, - f.field_type -FROM fields as f + fd.id, + fd.credential_id, + fd.field_name, + fv.field_value, + fd.field_type +FROM field_data as fd +JOIN field_values as fv ON fd.id = fv.field_id WHERE -field_type != 'sensitive' -AND f.user_id = $1 -AND f.credential_id = ANY($2::UUID[]) +(field_type = 'meta' OR field_type = 'additional') +AND fv.user_id = $1 +AND fd.field_type != 'totp' +AND fd.credential_id = ANY($2::UUID[]) ` type GetNonSensitiveFieldsForCredentialIDsParams struct { - UserID uuid.UUID `json:"userId"` - Credentials []uuid.UUID `json:"credentials"` + UserID uuid.UUID `json:"userId"` + Credentialids []uuid.UUID `json:"credentialids"` } type GetNonSensitiveFieldsForCredentialIDsRow struct { @@ -181,7 +222,7 @@ type GetNonSensitiveFieldsForCredentialIDsRow struct { } func (q *Queries) GetNonSensitiveFieldsForCredentialIDs(ctx context.Context, arg GetNonSensitiveFieldsForCredentialIDsParams) ([]GetNonSensitiveFieldsForCredentialIDsRow, error) { - rows, err := q.db.QueryContext(ctx, getNonSensitiveFieldsForCredentialIDs, arg.UserID, pq.Array(arg.Credentials)) + rows, err := q.db.QueryContext(ctx, getNonSensitiveFieldsForCredentialIDs, arg.UserID, pq.Array(arg.Credentialids)) if err != nil { return nil, err } @@ -211,14 +252,16 @@ func (q *Queries) GetNonSensitiveFieldsForCredentialIDs(ctx context.Context, arg const getSensitiveFields = `-- name: GetSensitiveFields :many SELECT - f.id, - f.field_name, - f.field_value -FROM fields as f + fd.id, + fd.field_name, + fv.field_value, + fd.field_type +FROM field_data as fd +JOIN field_values as fv ON fd.id = fv.field_id WHERE -field_type = 'sensitive' -AND f.credential_id = $1 -AND f.user_id = $2 +(fd.field_type = 'sensitive' OR fd.field_type = 'totp') +AND fd.credential_id = $1 +AND fv.user_id = $2 ` type GetSensitiveFieldsParams struct { @@ -230,6 +273,7 @@ type GetSensitiveFieldsRow struct { ID uuid.UUID `json:"id"` FieldName string `json:"fieldName"` FieldValue string `json:"fieldValue"` + FieldType string `json:"fieldType"` } func (q *Queries) GetSensitiveFields(ctx context.Context, arg GetSensitiveFieldsParams) ([]GetSensitiveFieldsRow, error) { @@ -241,7 +285,12 @@ func (q *Queries) GetSensitiveFields(ctx context.Context, arg GetSensitiveFields items := []GetSensitiveFieldsRow{} for rows.Next() { var i GetSensitiveFieldsRow - if err := rows.Scan(&i.ID, &i.FieldName, &i.FieldValue); err != nil { + if err := rows.Scan( + &i.ID, + &i.FieldName, + &i.FieldValue, + &i.FieldType, + ); err != nil { return nil, err } items = append(items, i) diff --git a/db/sqlc/field_data.sql.go b/db/sqlc/field_data.sql.go new file mode 100644 index 0000000..adbce3c --- /dev/null +++ b/db/sqlc/field_data.sql.go @@ -0,0 +1,49 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.26.0 +// source: field_data.sql + +package db + +import ( + "context" + + "github.com/google/uuid" + "github.com/lib/pq" +) + +const addFieldData = `-- name: AddFieldData :one +INSERT INTO field_data (field_name, field_type, credential_id, created_by, updated_by) +VALUES ($1, $2, $3, $4, $5) +RETURNING id +` + +type AddFieldDataParams struct { + FieldName string `json:"fieldName"` + FieldType string `json:"fieldType"` + CredentialID uuid.UUID `json:"credentialId"` + CreatedBy uuid.NullUUID `json:"createdBy"` + UpdatedBy uuid.NullUUID `json:"updatedBy"` +} + +func (q *Queries) AddFieldData(ctx context.Context, arg AddFieldDataParams) (uuid.UUID, error) { + row := q.db.QueryRowContext(ctx, addFieldData, + arg.FieldName, + arg.FieldType, + arg.CredentialID, + arg.CreatedBy, + arg.UpdatedBy, + ) + var id uuid.UUID + err := row.Scan(&id) + return id, err +} + +const deleteFields = `-- name: DeleteFields :exec +DELETE FROM field_data WHERE id = ANY($1::UUID[]) +` + +func (q *Queries) DeleteFields(ctx context.Context, fieldIds []uuid.UUID) error { + _, err := q.db.ExecContext(ctx, deleteFields, pq.Array(fieldIds)) + return err +} diff --git a/db/sqlc/field_values.sql.go b/db/sqlc/field_values.sql.go new file mode 100644 index 0000000..eed7f48 --- /dev/null +++ b/db/sqlc/field_values.sql.go @@ -0,0 +1,68 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.26.0 +// source: field_values.sql + +package db + +import ( + "context" + + "github.com/google/uuid" + "github.com/lib/pq" +) + +const addFieldValue = `-- name: AddFieldValue :one +INSERT INTO field_values (field_id, field_value, user_id) +VALUES ($1, $2, $3) RETURNING id +` + +type AddFieldValueParams struct { + FieldID uuid.UUID `json:"fieldId"` + FieldValue string `json:"fieldValue"` + UserID uuid.UUID `json:"userId"` +} + +func (q *Queries) AddFieldValue(ctx context.Context, arg AddFieldValueParams) (uuid.UUID, error) { + row := q.db.QueryRowContext(ctx, addFieldValue, arg.FieldID, arg.FieldValue, arg.UserID) + var id uuid.UUID + err := row.Scan(&id) + return id, err +} + +const getFieldValueIDsForFieldIDs = `-- name: GetFieldValueIDsForFieldIDs :many +SELECT id, field_id FROM field_values WHERE field_id = ANY($2::UUID[]) and user_id = $1 +` + +type GetFieldValueIDsForFieldIDsParams struct { + UserID uuid.UUID `json:"userId"` + Fieldids []uuid.UUID `json:"fieldids"` +} + +type GetFieldValueIDsForFieldIDsRow struct { + ID uuid.UUID `json:"id"` + FieldID uuid.UUID `json:"fieldId"` +} + +func (q *Queries) GetFieldValueIDsForFieldIDs(ctx context.Context, arg GetFieldValueIDsForFieldIDsParams) ([]GetFieldValueIDsForFieldIDsRow, error) { + rows, err := q.db.QueryContext(ctx, getFieldValueIDsForFieldIDs, arg.UserID, pq.Array(arg.Fieldids)) + if err != nil { + return nil, err + } + defer rows.Close() + items := []GetFieldValueIDsForFieldIDsRow{} + for rows.Next() { + var i GetFieldValueIDsForFieldIDsRow + if err := rows.Scan(&i.ID, &i.FieldID); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} diff --git a/db/sqlc/folder.sql.go b/db/sqlc/folder.sql.go index ab1dea2..0853ccd 100644 --- a/db/sqlc/folder.sql.go +++ b/db/sqlc/folder.sql.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.24.0 +// sqlc v1.26.0 // source: folder.sql package db @@ -14,8 +14,8 @@ import ( ) const addFolder = `-- name: AddFolder :one -INSERT INTO folders (name, description, created_by) -VALUES ($1, $2, $3) +INSERT INTO folders (name, description, created_by, type) +VALUES ($1, $2, $3, $4) RETURNING id, created_at ` @@ -23,6 +23,7 @@ type AddFolderParams struct { Name string `json:"name"` Description sql.NullString `json:"description"` CreatedBy uuid.NullUUID `json:"createdBy"` + Type string `json:"type"` } type AddFolderRow struct { @@ -31,7 +32,12 @@ type AddFolderRow struct { } func (q *Queries) AddFolder(ctx context.Context, arg AddFolderParams) (AddFolderRow, error) { - row := q.db.QueryRowContext(ctx, addFolder, arg.Name, arg.Description, arg.CreatedBy) + row := q.db.QueryRowContext(ctx, addFolder, + arg.Name, + arg.Description, + arg.CreatedBy, + arg.Type, + ) var i AddFolderRow err := row.Scan(&i.ID, &i.CreatedAt) return i, err @@ -55,7 +61,7 @@ func (q *Queries) EditFolder(ctx context.Context, arg EditFolderParams) error { } const fetchAccessibleFoldersForUser = `-- name: FetchAccessibleFoldersForUser :many -SELECT folders.id, folders.name, folders.description, folders.created_at, folders.created_by, COALESCE(folder_access.access_type, 'none') as "accessType" +SELECT folders.id, folders.name, folders.created_at, folders.created_by, COALESCE(folder_access.access_type, 'none') as "accessType", folders.type, COALESCE(folders.description, '') as "description" FROM folders LEFT JOIN folder_access ON folders.id = folder_access.folder_id AND folder_access.user_id = $1 WHERE folders.id IN ( @@ -71,12 +77,13 @@ WHERE folders.id IN ( ` type FetchAccessibleFoldersForUserRow struct { - ID uuid.UUID `json:"id"` - Name string `json:"name"` - Description sql.NullString `json:"description"` - CreatedAt time.Time `json:"createdAt"` - CreatedBy uuid.NullUUID `json:"createdBy"` - AccessType string `json:"accessType"` + ID uuid.UUID `json:"id"` + Name string `json:"name"` + CreatedAt time.Time `json:"createdAt"` + CreatedBy uuid.NullUUID `json:"createdBy"` + AccessType string `json:"accessType"` + Type string `json:"type"` + Description string `json:"description"` } func (q *Queries) FetchAccessibleFoldersForUser(ctx context.Context, userID uuid.UUID) ([]FetchAccessibleFoldersForUserRow, error) { @@ -91,10 +98,11 @@ func (q *Queries) FetchAccessibleFoldersForUser(ctx context.Context, userID uuid if err := rows.Scan( &i.ID, &i.Name, - &i.Description, &i.CreatedAt, &i.CreatedBy, &i.AccessType, + &i.Type, + &i.Description, ); err != nil { return nil, err } diff --git a/db/sqlc/folder_access.sql.go b/db/sqlc/folder_access.sql.go index 2bd5a5f..eeb76fa 100644 --- a/db/sqlc/folder_access.sql.go +++ b/db/sqlc/folder_access.sql.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.24.0 +// sqlc v1.26.0 // source: folder_access.sql package db diff --git a/db/sqlc/folder_transactions.go b/db/sqlc/folder_transactions.go index ef5b65f..e7d7108 100644 --- a/db/sqlc/folder_transactions.go +++ b/db/sqlc/folder_transactions.go @@ -12,6 +12,7 @@ type CreateFolderTransactionParams struct { Name string `json:"name"` Description sql.NullString `json:"description"` CreatedBy uuid.UUID `json:"createdBy"` + SuperUser *uuid.UUID `json:"superUser"` } func (store *SQLStore) CreateFolderTransaction(ctx context.Context, args CreateFolderTransactionParams) (dto.FolderDetails, error) { @@ -24,6 +25,10 @@ func (store *SQLStore) CreateFolderTransaction(ctx context.Context, args CreateF Name: args.Name, Description: args.Description, CreatedBy: uuid.NullUUID{UUID: args.CreatedBy, Valid: true}, + Type: "shared", + } + if args.SuperUser == nil { + addFolderParams.Type = "private" } folderData, err := q.AddFolder(ctx, addFolderParams) if err != nil { @@ -36,6 +41,13 @@ func (store *SQLStore) CreateFolderTransaction(ctx context.Context, args CreateF UserID: args.CreatedBy, AccessType: "manager", }) + if args.SuperUser != nil && args.CreatedBy != *args.SuperUser { + err = q.AddFolderAccess(ctx, AddFolderAccessParams{ + FolderID: folderData.ID, + UserID: *args.SuperUser, + AccessType: "manager", + }) + } if err != nil { return err } diff --git a/db/sqlc/group_transactions.go b/db/sqlc/group_transactions.go index 7052c1c..2825956 100644 --- a/db/sqlc/group_transactions.go +++ b/db/sqlc/group_transactions.go @@ -39,7 +39,7 @@ func (store *SQLStore) CreateGroupAndAddManager(ctx context.Context, groupData d } type AddMembersToGroupTransactionParams struct { - FieldArgs []AddFieldParams `json:"fieldArgs"` + FieldArgs []AddFieldValueParams `json:"fieldArgs"` CredentialAccessArgs []AddCredentialAccessParams `json:"credentialAccessArgs"` FolderAccessArgs []AddFolderAccessParams `json:"folderAccessArgs"` GroupMembershipArgs []AddGroupMemberParams `json:"groupMembershipArgs"` @@ -51,7 +51,7 @@ func (store *SQLStore) AddMembersToGroupTransaction(ctx context.Context, args Ad for _, fieldRecord := range args.FieldArgs { - _, err := q.AddField(ctx, fieldRecord) + _, err := q.AddFieldValue(ctx, fieldRecord) if err != nil { return err } diff --git a/db/sqlc/groups.sql.go b/db/sqlc/groups.sql.go index a5523f1..4f6e5ff 100644 --- a/db/sqlc/groups.sql.go +++ b/db/sqlc/groups.sql.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.24.0 +// sqlc v1.26.0 // source: groups.sql package db @@ -417,7 +417,7 @@ const getUsersWithoutGroupAccess = `-- name: GetUsersWithoutGroupAccess :many SELECT u.id, u.username, u.name, COALESCE(u.encryption_key,'') as "publicKey" FROM users u LEFT JOIN group_list gl ON (u.id = gl.user_id AND gl.grouping_id = $1) -WHERE gl.grouping_id IS NULL and u.status = 'active' +WHERE gl.grouping_id IS NULL and u.status = 'active' and u.type != 'cli' ` type GetUsersWithoutGroupAccessRow struct { diff --git a/db/sqlc/models.go b/db/sqlc/models.go index b8b168f..ff621c4 100644 --- a/db/sqlc/models.go +++ b/db/sqlc/models.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.24.0 +// sqlc v1.26.0 package db @@ -35,6 +35,26 @@ type CredentialAccess struct { FolderID uuid.NullUUID `json:"folderId"` } +type Environment struct { + ID uuid.UUID `json:"id"` + CliUser uuid.UUID `json:"cliUser"` + Name string `json:"name"` + Createdat time.Time `json:"createdat"` + Updatedat time.Time `json:"updatedat"` + CreatedBy uuid.UUID `json:"createdBy"` +} + +type EnvironmentField struct { + ID uuid.UUID `json:"id"` + FieldName string `json:"fieldName"` + FieldValue string `json:"fieldValue"` + ParentFieldValueID uuid.UUID `json:"parentFieldValueId"` + EnvID uuid.UUID `json:"envId"` + CredentialID uuid.UUID `json:"credentialId"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` +} + type Field struct { ID uuid.UUID `json:"id"` FieldName string `json:"fieldName"` @@ -48,6 +68,24 @@ type Field struct { UpdatedBy uuid.NullUUID `json:"updatedBy"` } +type FieldDatum struct { + ID uuid.UUID `json:"id"` + FieldName string `json:"fieldName"` + FieldType string `json:"fieldType"` + CredentialID uuid.UUID `json:"credentialId"` + CreatedAt time.Time `json:"createdAt"` + CreatedBy uuid.NullUUID `json:"createdBy"` + UpdatedAt time.Time `json:"updatedAt"` + UpdatedBy uuid.NullUUID `json:"updatedBy"` +} + +type FieldValue struct { + ID uuid.UUID `json:"id"` + FieldID uuid.UUID `json:"fieldId"` + FieldValue string `json:"fieldValue"` + UserID uuid.UUID `json:"userId"` +} + type Folder struct { ID uuid.UUID `json:"id"` CreatedAt time.Time `json:"createdAt"` @@ -55,6 +93,7 @@ type Folder struct { Name string `json:"name"` Description sql.NullString `json:"description"` CreatedBy uuid.NullUUID `json:"createdBy"` + Type string `json:"type"` } type FolderAccess struct { @@ -107,4 +146,5 @@ type User struct { SignedUp bool `json:"signedUp"` Type string `json:"type"` Status string `json:"status"` + CreatedBy uuid.NullUUID `json:"createdBy"` } diff --git a/db/sqlc/querier.go b/db/sqlc/querier.go index ba20a24..bcf0d23 100644 --- a/db/sqlc/querier.go +++ b/db/sqlc/querier.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.24.0 +// sqlc v1.26.0 package db @@ -13,11 +13,17 @@ import ( type Querier interface { AddCredentialAccess(ctx context.Context, arg AddCredentialAccessParams) (uuid.UUID, error) + AddEnvironment(ctx context.Context, arg AddEnvironmentParams) (uuid.UUID, error) AddField(ctx context.Context, arg AddFieldParams) (uuid.UUID, error) + AddFieldData(ctx context.Context, arg AddFieldDataParams) (uuid.UUID, error) + AddFieldValue(ctx context.Context, arg AddFieldValueParams) (uuid.UUID, error) AddFolder(ctx context.Context, arg AddFolderParams) (AddFolderRow, error) AddFolderAccess(ctx context.Context, arg AddFolderAccessParams) error AddGroupMember(ctx context.Context, arg AddGroupMemberParams) error + CheckAnyCredentialAccessEntryExists(ctx context.Context, arg CheckAnyCredentialAccessEntryExistsParams) (bool, error) + CheckCliUser(ctx context.Context, id uuid.UUID) (bool, error) CheckCredentialAccessEntryExists(ctx context.Context, arg CheckCredentialAccessEntryExistsParams) (bool, error) + CheckCredentialExistsForEnv(ctx context.Context, arg CheckCredentialExistsForEnvParams) (bool, error) CheckFieldEntryExists(ctx context.Context, arg CheckFieldEntryExistsParams) (bool, error) CheckFolderAccessEntryExists(ctx context.Context, arg CheckFolderAccessEntryExistsParams) (bool, error) CheckIfUsersExist(ctx context.Context) (bool, error) @@ -26,17 +32,24 @@ type Querier interface { CheckUserMemberOfGroup(ctx context.Context, arg CheckUserMemberOfGroupParams) (bool, error) CheckUsernameExist(ctx context.Context, username string) (bool, error) CreateChallenge(ctx context.Context, arg CreateChallengeParams) (SessionTable, error) + CreateCliUser(ctx context.Context, arg CreateCliUserParams) (uuid.UUID, error) // sql/create_credential.sql CreateCredential(ctx context.Context, arg CreateCredentialParams) (uuid.UUID, error) + CreateEnvFields(ctx context.Context, arg CreateEnvFieldsParams) (uuid.UUID, error) CreateGroup(ctx context.Context, arg CreateGroupParams) (CreateGroupRow, error) CreateUser(ctx context.Context, arg CreateUserParams) (uuid.UUID, error) DeleteAccessRemovedFields(ctx context.Context) error DeleteCredentialFields(ctx context.Context, credentialID uuid.UUID) error + DeleteFields(ctx context.Context, fieldIds []uuid.UUID) error EditCredentialAccessForGroup(ctx context.Context, arg EditCredentialAccessForGroupParams) error EditCredentialAccessForGroupWithFolderID(ctx context.Context, arg EditCredentialAccessForGroupWithFolderIDParams) error EditCredentialAccessForUser(ctx context.Context, arg EditCredentialAccessForUserParams) error EditCredentialAccessForUserWithFolderID(ctx context.Context, arg EditCredentialAccessForUserWithFolderIDParams) error EditCredentialDetails(ctx context.Context, arg EditCredentialDetailsParams) error + EditEnvFieldValue(ctx context.Context, arg EditEnvFieldValueParams) error + EditEnvironmentFieldNameByID(ctx context.Context, arg EditEnvironmentFieldNameByIDParams) (string, error) + EditFieldData(ctx context.Context, arg EditFieldDataParams) error + EditFieldValue(ctx context.Context, arg EditFieldValueParams) error EditFolder(ctx context.Context, arg EditFolderParams) error EditFolderAccessForGroup(ctx context.Context, arg EditFolderAccessForGroupParams) error EditFolderAccessForUser(ctx context.Context, arg EditFolderAccessForUserParams) error @@ -54,6 +67,7 @@ type Querier interface { GetAllSignedUpUsers(ctx context.Context) ([]GetAllSignedUpUsersRow, error) GetAllUrlsForUser(ctx context.Context, userID uuid.UUID) ([]GetAllUrlsForUserRow, error) GetAllUsers(ctx context.Context) ([]GetAllUsersRow, error) + GetCliUsers(ctx context.Context, createdBy uuid.NullUUID) ([]GetCliUsersRow, error) //----------------------------------------------------------------------------------------------------- GetCredentialAccessDetailsWithGroupAccess(ctx context.Context, groupID uuid.NullUUID) ([]GetCredentialAccessDetailsWithGroupAccessRow, error) GetCredentialAccessTypeForUser(ctx context.Context, arg GetCredentialAccessTypeForUserParams) ([]GetCredentialAccessTypeForUserRow, error) @@ -65,6 +79,13 @@ type Querier interface { GetCredentialUsersForDataSync(ctx context.Context, credentialID uuid.UUID) ([]GetCredentialUsersForDataSyncRow, error) GetCredentialUsersWithDirectAccess(ctx context.Context, credentialID uuid.UUID) ([]GetCredentialUsersWithDirectAccessRow, error) GetCredentialsForSearchByUserID(ctx context.Context, userID uuid.UUID) ([]GetCredentialsForSearchByUserIDRow, error) + GetEnvFields(ctx context.Context, envID uuid.UUID) ([]GetEnvFieldsRow, error) + GetEnvFieldsForCredential(ctx context.Context, credentialID uuid.UUID) ([]GetEnvFieldsForCredentialRow, error) + GetEnvForCredential(ctx context.Context, credentialID uuid.UUID) ([]GetEnvForCredentialRow, error) + GetEnvironmentByID(ctx context.Context, arg GetEnvironmentByIDParams) (Environment, error) + GetEnvironmentFieldsByName(ctx context.Context, name string) ([]GetEnvironmentFieldsByNameRow, error) + GetEnvironmentsForUser(ctx context.Context, createdBy uuid.NullUUID) ([]GetEnvironmentsForUserRow, error) + GetFieldValueIDsForFieldIDs(ctx context.Context, arg GetFieldValueIDsForFieldIDsParams) ([]GetFieldValueIDsForFieldIDsRow, error) GetFolderAccessForUser(ctx context.Context, arg GetFolderAccessForUserParams) ([]string, error) GetFolderGroups(ctx context.Context, folderID uuid.UUID) ([]GetFolderGroupsRow, error) GetFolderIDAndTypeWithGroupAccess(ctx context.Context, groupID uuid.NullUUID) ([]GetFolderIDAndTypeWithGroupAccessRow, error) @@ -76,10 +97,12 @@ type Querier interface { GetRegistrationChallenge(ctx context.Context, username string) (GetRegistrationChallengeRow, error) GetSensitiveFields(ctx context.Context, arg GetSensitiveFieldsParams) ([]GetSensitiveFieldsRow, error) GetSharedGroupsForFolder(ctx context.Context, folderID uuid.UUID) ([]GetSharedGroupsForFolderRow, error) + GetSuperUser(ctx context.Context) (User, error) GetUserByID(ctx context.Context, id uuid.UUID) (GetUserByIDRow, error) GetUserByPublicKey(ctx context.Context, deviceKey sql.NullString) (uuid.UUID, error) GetUserByUsername(ctx context.Context, username string) (GetUserByUsernameRow, error) GetUserDeviceKey(ctx context.Context, id uuid.UUID) (string, error) + GetUserEnvsForCredential(ctx context.Context, arg GetUserEnvsForCredentialParams) ([]uuid.UUID, error) GetUserTempPassword(ctx context.Context, username string) (GetUserTempPasswordRow, error) GetUserType(ctx context.Context, id uuid.UUID) (string, error) GetUsersByCredential(ctx context.Context, credentialID uuid.UUID) ([]GetUsersByCredentialRow, error) @@ -88,6 +111,7 @@ type Querier interface { HasManageAccessForFolder(ctx context.Context, arg HasManageAccessForFolderParams) (bool, error) HasReadAccessForCredential(ctx context.Context, arg HasReadAccessForCredentialParams) (bool, error) HasReadAccessForFolder(ctx context.Context, arg HasReadAccessForFolderParams) (bool, error) + IsEnvironmentOwner(ctx context.Context, arg IsEnvironmentOwnerParams) (bool, error) IsUserManagerOrOwner(ctx context.Context, arg IsUserManagerOrOwnerParams) (bool, error) RemoveCredential(ctx context.Context, id uuid.UUID) error RemoveCredentialAccessForGroupMember(ctx context.Context, arg RemoveCredentialAccessForGroupMemberParams) error diff --git a/db/sqlc/share_credential_transactions.go b/db/sqlc/share_credential_transactions.go index a38c6d4..2a785ca 100644 --- a/db/sqlc/share_credential_transactions.go +++ b/db/sqlc/share_credential_transactions.go @@ -5,7 +5,7 @@ import ( ) type ShareCredentialTransactionParams struct { - FieldArgs []AddFieldParams `json:"fieldArgs"` + FieldArgs []AddFieldValueParams `json:"fieldArgs"` CredentialAccessArgs []AddCredentialAccessParams `json:"credentialAccessArgs"` FolderAccessArgs []AddFolderAccessParams `json:"folderAccessArgs"` } @@ -16,7 +16,7 @@ func (store *SQLStore) ShareCredentialsTransaction(ctx context.Context, args Sha for _, fieldRecord := range args.FieldArgs { - _, err := q.AddField(ctx, fieldRecord) + _, err := q.AddFieldValue(ctx, fieldRecord) if err != nil { return err } diff --git a/db/sqlc/store.go b/db/sqlc/store.go index 611a2f1..58629c9 100644 --- a/db/sqlc/store.go +++ b/db/sqlc/store.go @@ -15,12 +15,13 @@ type Store interface { CreateGroupAndAddManager(context.Context, dto.GroupDetails) (dto.GroupDetails, error) AddMembersToGroupTransaction(context.Context, AddMembersToGroupTransactionParams) error ShareCredentialsTransaction(context.Context, ShareCredentialTransactionParams) error - EditCredentialTransaction(context.Context, EditCredentialTransactionParams) error + EditCredentialTransaction(context.Context, dto.EditCredentialRequest, uuid.UUID) error RemoveFolderAccessForUsersTransactions(context.Context, RemoveFolderAccessForUsersParams) error RemoveFolderAccessForGroupsTransactions(context.Context, RemoveFolderAccessForGroupsParams) error EditFolderAccessForUserTransaction(context.Context, EditFolderAccessForUserParams) error EditFolderAccessForGroupTransaction(context.Context, EditFolderAccessForGroupParams) error RemoveMemberFromGroupTransaction(context.Context, RemoveMemberFromGroupTransactionParams) error + AddCredentialFieldToEnvTxn(context.Context, []dto.CredentialEnvData) error } type SQLStore struct { diff --git a/db/sqlc/user.sql.go b/db/sqlc/user.sql.go index 88cc8c9..b4477b7 100644 --- a/db/sqlc/user.sql.go +++ b/db/sqlc/user.sql.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.24.0 +// sqlc v1.26.0 // source: user.sql package db @@ -12,6 +12,17 @@ import ( "github.com/google/uuid" ) +const checkCliUser = `-- name: CheckCliUser :one +SELECT EXISTS(SELECT 1 FROM users WHERE id = $1 AND type = 'cli') +` + +func (q *Queries) CheckCliUser(ctx context.Context, id uuid.UUID) (bool, error) { + row := q.db.QueryRowContext(ctx, checkCliUser, id) + var exists bool + err := row.Scan(&exists) + return exists, err +} + const checkIfUsersExist = `-- name: CheckIfUsersExist :one SELECT EXISTS(SELECT 1 FROM users) ` @@ -78,6 +89,65 @@ func (q *Queries) CreateChallenge(ctx context.Context, arg CreateChallengeParams return i, err } +const createCliUser = `-- name: CreateCliUser :one +INSERT INTO users ( + + username, + name, + encryption_key, + device_key, + temp_password, + registration_challenge, + signed_up, + type, + status, + created_by +) VALUES ( + $1, + $2, + $3, + $4, + $5, + $6, + $7, + $8, + $9, + $10 +) +RETURNING id +` + +type CreateCliUserParams struct { + Username string `json:"username"` + Name string `json:"name"` + EncryptionKey sql.NullString `json:"encryptionKey"` + DeviceKey sql.NullString `json:"deviceKey"` + TempPassword string `json:"tempPassword"` + RegistrationChallenge sql.NullString `json:"registrationChallenge"` + SignedUp bool `json:"signedUp"` + Type string `json:"type"` + Status string `json:"status"` + CreatedBy uuid.NullUUID `json:"createdBy"` +} + +func (q *Queries) CreateCliUser(ctx context.Context, arg CreateCliUserParams) (uuid.UUID, error) { + row := q.db.QueryRowContext(ctx, createCliUser, + arg.Username, + arg.Name, + arg.EncryptionKey, + arg.DeviceKey, + arg.TempPassword, + arg.RegistrationChallenge, + arg.SignedUp, + arg.Type, + arg.Status, + arg.CreatedBy, + ) + var id uuid.UUID + err := row.Scan(&id) + return id, err +} + const createUser = `-- name: CreateUser :one INSERT INTO users (username, name, temp_password, type) VALUES ($1, $2, $3, COALESCE($4, 'user')) @@ -115,7 +185,7 @@ func (q *Queries) FetchChallenge(ctx context.Context, userID uuid.UUID) (string, } const getAllSignedUpUsers = `-- name: GetAllSignedUpUsers :many -SELECT id,name,username, COALESCE(encryption_key, '') AS "publicKey" FROM users where signed_up = true +SELECT id,name,username, COALESCE(encryption_key, '') AS "publicKey" FROM users where signed_up = true and type !='cli' ` type GetAllSignedUpUsersRow struct { @@ -194,6 +264,38 @@ func (q *Queries) GetAllUsers(ctx context.Context) ([]GetAllUsersRow, error) { return items, nil } +const getCliUsers = `-- name: GetCliUsers :many +SELECT id, username FROM users WHERE type = 'cli' and created_by = $1 +` + +type GetCliUsersRow struct { + ID uuid.UUID `json:"id"` + Username string `json:"username"` +} + +func (q *Queries) GetCliUsers(ctx context.Context, createdBy uuid.NullUUID) ([]GetCliUsersRow, error) { + rows, err := q.db.QueryContext(ctx, getCliUsers, createdBy) + if err != nil { + return nil, err + } + defer rows.Close() + items := []GetCliUsersRow{} + for rows.Next() { + var i GetCliUsersRow + if err := rows.Scan(&i.ID, &i.Username); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + const getRegistrationChallenge = `-- name: GetRegistrationChallenge :one SELECT registration_challenge, status FROM users WHERE username = $1 ` @@ -210,6 +312,31 @@ func (q *Queries) GetRegistrationChallenge(ctx context.Context, username string) return i, err } +const getSuperUser = `-- name: GetSuperUser :one +select id, created_at, updated_at, username, name, encryption_key, device_key, temp_password, registration_challenge, signed_up, type, status, created_by from users where type = 'superadmin' limit 1 +` + +func (q *Queries) GetSuperUser(ctx context.Context) (User, error) { + row := q.db.QueryRowContext(ctx, getSuperUser) + var i User + err := row.Scan( + &i.ID, + &i.CreatedAt, + &i.UpdatedAt, + &i.Username, + &i.Name, + &i.EncryptionKey, + &i.DeviceKey, + &i.TempPassword, + &i.RegistrationChallenge, + &i.SignedUp, + &i.Type, + &i.Status, + &i.CreatedBy, + ) + return i, err +} + const getUserByID = `-- name: GetUserByID :one SELECT id, username, name, type FROM users WHERE id = $1 ` diff --git a/deploy.sh b/deploy.sh index a93d9ec..0b82fdb 100755 --- a/deploy.sh +++ b/deploy.sh @@ -4,7 +4,7 @@ ssh ubuntu@3.110.128.10 << 'EOF' cd osvauld_be - git checkout main + git checkout develop git pull diff --git a/dtos/credential_dto.go b/dtos/credential_dto.go index 95a285e..8f660a3 100644 --- a/dtos/credential_dto.go +++ b/dtos/credential_dto.go @@ -7,12 +7,12 @@ import ( ) type AddCredentialRequest struct { - Name string `json:"name"` - Description string `json:"description"` - FolderID uuid.UUID `json:"folderId"` - CredentialType string `json:"credentialType"` - UserFields []UserFields `json:"userFields"` - Domain string `json:"domain"` + Name string `json:"name"` + Description string `json:"description"` + FolderID uuid.UUID `json:"folderId"` + CredentialType string `json:"credentialType"` + Fields []Fields `json:"fields"` + Domain string `json:"domain"` } type CredentialForUser struct { @@ -71,11 +71,38 @@ type ShareFolderWithGroupsRequest struct { GroupData []CredentialsForGroupsPayload `json:"groupData" binding:"required"` } +type EditedEnvField struct { + EnvFieldID uuid.UUID `json:"envFieldId"` + FieldValue string `json:"fieldValue"` +} + +type NewEnvField struct { + EnvID uuid.UUID `json:"envId"` + FieldValue string `json:"fieldValue"` +} + +type UserFieldValueWithCliUserValues struct { + UserID uuid.UUID `json:"userId"` + FieldValue string `json:"fieldValue"` + EnvFieldValues []NewEnvField `json:"envFieldValues"` +} + +type NewFieldsWithEnvValues struct { + FieldName string `json:"fieldName"` + FieldType string `json:"fieldType"` + FieldValues []UserFieldValueWithCliUserValues `jNewson:"fieldValues"` +} + type EditCredentialRequest struct { - Name string `json:"name"` - Description string `json:"description"` - CredentialType string `json:"credentialType"` - UserFields []UserFields `json:"userFields"` + CredentialID uuid.UUID `json:"credentialId"` + Name string `json:"name"` + Description string `json:"description"` + CredentialType string `json:"credentialType"` + EditedUserFields []Fields `json:"editedUserFields"` + EditedEnvFields []EditedEnvField `json:"editedEnvFields"` + NewFields []NewFieldsWithEnvValues `json:"newFields"` + DeletedFields []uuid.UUID `json:"deletedFields"` + Domain string `json:"domain"` } type EditCredentialDetailsRequest struct { diff --git a/dtos/env_dto.go b/dtos/env_dto.go new file mode 100644 index 0000000..b290d33 --- /dev/null +++ b/dtos/env_dto.go @@ -0,0 +1,52 @@ +package dto + +import "github.com/google/uuid" + +// type EnvField struct { +// ID uuid.UUID `json:"id"` +// FieldName string `json:"fieldName"` +// FieldValue string `json:"fieldValue"` +// } + +type EnvField struct { + FieldID uuid.UUID `json:"fieldId"` + FieldValue string `json:"fieldValue"` +} + +type CredentialsData struct { + CredentialID uuid.UUID `json:"credentialId"` + Fields []EnvField `json:"fields"` +} + +type ShareCredentialsWithEnvironmentRequest struct { + EnvId uuid.UUID `json:"envId"` + Credentials []CredentialsData `json:"credentials"` +} + +type CredentialEnvData struct { + CredentialID uuid.UUID + EnvID uuid.UUID + ParentFieldValueID uuid.UUID + FieldValue string + FieldName string +} + +// Todo: later merge this struct and EnvField stuct +type EnvFieldData struct { + FieldID uuid.UUID `json:"fieldId"` + FieldName string `json:"fieldName"` + FieldValue string `json:"fieldValue"` +} + +type CredentialEnvFields struct { + CredentialID uuid.UUID `json:"credentialId"` + CredentialName string `json:"credentialName"` + Fields []EnvFieldData `json:"fields"` +} + +type CredentialEnvFieldsForEditDataSync struct { + EnvFieldID uuid.UUID `json:"envFieldId"` + EnvID uuid.UUID `json:"envId"` + CliUserID uuid.UUID `json:"cliUserId"` + CliUserPublicKey string `json:"cliUserPublicKey"` +} diff --git a/dtos/field_dto.go b/dtos/field_dto.go index 4310728..d0ef5ad 100644 --- a/dtos/field_dto.go +++ b/dtos/field_dto.go @@ -23,3 +23,16 @@ type CredentialFields struct { CredentialID uuid.UUID `json:"credentialId"` Fields []Field `json:"fields"` } + +type Fields struct { + FieldID uuid.UUID `json:"fieldId"` + FieldName string `json:"fieldName"` + FieldType string `json:"fieldType"` + FieldValues []FieldValue `json:"fieldValues"` +} + + +type FieldValue struct { + UserID uuid.UUID `json:"userId"` + FieldValue string `json:"fieldValue"` +} diff --git a/dtos/folder_dto.go b/dtos/folder_dto.go index b178481..9f5dddb 100644 --- a/dtos/folder_dto.go +++ b/dtos/folder_dto.go @@ -7,8 +7,9 @@ import ( ) type CreateFolderRequest struct { - Name string `json:"name"` - Description string `json:"description"` + Name string `json:"name"` + Description string `json:"description"` + SharedFolder bool `json:"sharedFolder"` } type FolderDetails struct { diff --git a/dtos/user_dto.go b/dtos/user_dto.go index 371820c..7564769 100644 --- a/dtos/user_dto.go +++ b/dtos/user_dto.go @@ -1,5 +1,7 @@ package dto +import "github.com/google/uuid" + type CreateUser struct { UserName string `json:"username" binding:"required"` Name string `json:"name" binding:"required"` @@ -37,3 +39,20 @@ type CheckUserAvailability struct { UserName string `json:"username" binding:"required"` Name string `json:"name" binding:"required"` } + +type CreateCLIUser struct { + Name string `json:"name" binding:"required"` + DeviceKey string `json:"deviceKey" binding:"required"` + EncryptionKey string `json:"encryptionKey" binding:"required"` +} + +type AddEnvironment struct { + Name string `json:"name" binding:"required"` + CliUser uuid.UUID `json:"cliUser" binding:"required"` +} + +type EditEnvFieldName struct { + FieldID uuid.UUID `json:"fieldID" binding:"required"` + FieldName string `json:"fieldName" binding:"required"` + EnvironmentID uuid.UUID `json:"environmentID" binding:"required"` +} diff --git a/repository/credential_access_repo.go b/repository/credential_access_repo.go index 5b9bdcf..67d2cec 100644 --- a/repository/credential_access_repo.go +++ b/repository/credential_access_repo.go @@ -16,6 +16,10 @@ func CheckCredentialAccessEntryExists(ctx *gin.Context, args db.CheckCredentialA return database.Store.CheckCredentialAccessEntryExists(ctx, args) } +func CheckAnyCredentialAccessEntryExists(ctx *gin.Context, args db.CheckAnyCredentialAccessEntryExistsParams) (bool, error) { + return database.Store.CheckAnyCredentialAccessEntryExists(ctx, args) +} + func GetCredentialAccessTypeForUser(ctx *gin.Context, args db.GetCredentialAccessTypeForUserParams) ([]db.GetCredentialAccessTypeForUserRow, error) { return database.Store.GetCredentialAccessTypeForUser(ctx, args) } diff --git a/repository/credential_repository.go b/repository/credential_repository.go index 52798d3..6ce9de4 100644 --- a/repository/credential_repository.go +++ b/repository/credential_repository.go @@ -2,6 +2,7 @@ package repository import ( db "osvauld/db/sqlc" + dto "osvauld/dtos" "osvauld/infra/database" "github.com/gin-gonic/gin" @@ -20,8 +21,8 @@ func FetchCredentialDetailsForUserByFolderId(ctx *gin.Context, args db.FetchCred return database.Store.FetchCredentialDetailsForUserByFolderId(ctx, args) } -func EditCredential(ctx *gin.Context, args db.EditCredentialTransactionParams) error { - return database.Store.EditCredentialTransaction(ctx, args) +func EditCredential(ctx *gin.Context, args dto.EditCredentialRequest, caller uuid.UUID) error { + return database.Store.EditCredentialTransaction(ctx, args, caller) } func EditCredentialDetails(ctx *gin.Context, args db.EditCredentialDetailsParams) error { diff --git a/repository/environment_repo.go b/repository/environment_repo.go new file mode 100644 index 0000000..138b2e5 --- /dev/null +++ b/repository/environment_repo.go @@ -0,0 +1,55 @@ +package repository + +import ( + db "osvauld/db/sqlc" + dto "osvauld/dtos" + "osvauld/infra/database" + + "github.com/gin-gonic/gin" + "github.com/google/uuid" +) + +func AddEnvironment(ctx *gin.Context, args db.AddEnvironmentParams) (uuid.UUID, error) { + return database.Store.AddEnvironment(ctx, args) +} + +func CheckCredentialExistsInEnvironment(ctx *gin.Context, credentialID uuid.UUID, environmentID uuid.UUID) (bool, error) { + return database.Store.CheckCredentialExistsForEnv(ctx, db.CheckCredentialExistsForEnvParams{ + CredentialID: credentialID, + EnvID: environmentID, + }) +} + +func AddCredentialFieldsToEnvironment(ctx *gin.Context, args []dto.CredentialEnvData) error { + return database.Store.AddCredentialFieldToEnvTxn(ctx, args) +} + +func GetEnvironmentByID(ctx *gin.Context, environmentID uuid.UUID, caller uuid.UUID) (db.Environment, error) { + return database.Store.GetEnvironmentByID(ctx, db.GetEnvironmentByIDParams{ + ID: environmentID, + CreatedBy: caller, + }) +} +func GetEnvironmentFields(ctx *gin.Context, envID uuid.UUID) ([]db.GetEnvFieldsRow, error) { + return database.Store.GetEnvFields(ctx, envID) +} + +func GetEnvironmentFieldsByName(ctx *gin.Context, name string) ([]db.GetEnvironmentFieldsByNameRow, error) { + return database.Store.GetEnvironmentFieldsByName(ctx, name) +} + +func EditEnvironmentFieldName(ctx *gin.Context, args db.EditEnvironmentFieldNameByIDParams) (string, error) { + return database.Store.EditEnvironmentFieldNameByID(ctx, args) +} + +func IsEnvironmentOwner(ctx *gin.Context, args db.IsEnvironmentOwnerParams) (bool, error) { + return database.Store.IsEnvironmentOwner(ctx, args) +} + +func GetEnvFieldsForCredential(ctx *gin.Context, credentialID uuid.UUID) ([]db.GetEnvFieldsForCredentialRow, error) { + return database.Store.GetEnvFieldsForCredential(ctx, credentialID) +} + +func GetEnvForCredential(ctx *gin.Context, credentialId uuid.UUID) ([]db.GetEnvForCredentialRow, error) { + return database.Store.GetEnvForCredential(ctx, credentialId) +} diff --git a/repository/field_repo.go b/repository/field_repo.go index c46617a..a8d4fc2 100644 --- a/repository/field_repo.go +++ b/repository/field_repo.go @@ -26,3 +26,7 @@ func GetSensitiveFields(ctx *gin.Context, args db.GetSensitiveFieldsParams) ([]d func DeleteAccessRemovedFields(ctx *gin.Context) error { return database.Store.DeleteAccessRemovedFields(ctx) } + +func GetFieldValueIDsForFieldIDs(ctx *gin.Context, args db.GetFieldValueIDsForFieldIDsParams) ([]db.GetFieldValueIDsForFieldIDsRow, error) { + return database.Store.GetFieldValueIDsForFieldIDs(ctx, args) +} \ No newline at end of file diff --git a/repository/user_repository.go b/repository/user_repository.go index f25a83f..aa9400d 100644 --- a/repository/user_repository.go +++ b/repository/user_repository.go @@ -3,6 +3,7 @@ package repository import ( "database/sql" db "osvauld/db/sqlc" + dto "osvauld/dtos" "osvauld/infra/database" "github.com/gin-gonic/gin" @@ -77,3 +78,29 @@ func GetAllUsers(ctx *gin.Context) ([]db.GetAllUsersRow, error) { func GetUserDeviceKey(ctx *gin.Context, userID uuid.UUID) (string, error) { return database.Store.GetUserDeviceKey(ctx, userID) } + +func CreateCLIUser(ctx *gin.Context, userDetails dto.CreateCLIUser, caller uuid.UUID) (uuid.UUID, error) { + return database.Store.CreateCliUser(ctx, db.CreateCliUserParams{ + Name: userDetails.Name, + DeviceKey: sql.NullString{String: userDetails.DeviceKey, Valid: true}, + EncryptionKey: sql.NullString{String: userDetails.EncryptionKey, Valid: true}, + Username: userDetails.Name, + CreatedBy: uuid.NullUUID{UUID: caller, Valid: true}, + TempPassword: userDetails.Name, + Type: "cli", + Status: "active", + SignedUp: true, + }) +} + +func GetEnvironments(ctx *gin.Context, userID uuid.UUID) ([]db.GetEnvironmentsForUserRow, error) { + return database.Store.GetEnvironmentsForUser(ctx, uuid.NullUUID{UUID: userID, Valid: true}) +} + +func GetCliUsers(ctx *gin.Context, userID uuid.UUID) ([]db.GetCliUsersRow, error) { + return database.Store.GetCliUsers(ctx, uuid.NullUUID{UUID: userID, Valid: true}) +} + +func GetSuperUser(ctx *gin.Context) (db.User, error) { + return database.Store.GetSuperUser(ctx) +} diff --git a/routers/index.go b/routers/index.go index 31d91ae..e46bebf 100644 --- a/routers/index.go +++ b/routers/index.go @@ -18,9 +18,14 @@ func RegisterRoutes(route *gin.Engine) { route.GET("/health", func(ctx *gin.Context) { ctx.JSON(http.StatusOK, gin.H{"live": "ok"}) }) route.POST("/user/", middleware.JWTAuthMiddleware(), middleware.SignatureMiddleware(), controllers.CreateUser) route.GET("/user", middleware.JWTAuthMiddleware(), controllers.GetUser) + route.POST("user/environment", middleware.JWTAuthMiddleware(), controllers.AddEnvironment) + route.POST("/user/cli-user", middleware.JWTAuthMiddleware(), controllers.CreateCLIUser) + route.GET("/user/cli-users", middleware.JWTAuthMiddleware(), controllers.GetCliUsers) route.POST("/user/temp-login", controllers.TempLogin) route.POST("/user/name-availability", middleware.JWTAuthMiddleware(), controllers.CheckUserAvailability) route.DELETE("/user/:id", controllers.RemoveUserFromAll) + route.GET("/user/environments", middleware.JWTAuthMiddleware(), controllers.GetEnvironments) + route.GET("/user/environment/:id", middleware.JWTAuthMiddleware(), controllers.GetEnvironmentFields) route.POST("/user/register", controllers.Register) route.POST("/user/challenge", controllers.GetChallenge) route.POST("/user/verify", controllers.VerifyChallenge) @@ -28,10 +33,11 @@ func RegisterRoutes(route *gin.Engine) { route.GET("/users/all", middleware.JWTAuthMiddleware(), controllers.GetAllUsers) route.POST("/folder/", middleware.JWTAuthMiddleware(), controllers.CreateFolder) route.GET("/folder/:id/credential", middleware.JWTAuthMiddleware(), controllers.GetCredentialsByFolder) - route.GET("/folders/", middleware.JWTAuthMiddleware(), controllers.FetchAccessibleFoldersForUser) + route.GET("/folders", middleware.JWTAuthMiddleware(), controllers.FetchAccessibleFoldersForUser) route.POST("/share-credentials/users", middleware.JWTAuthMiddleware(), middleware.SignatureMiddleware(), controllers.ShareCredentialsWithUsers) route.POST("/share-credentials/groups", middleware.JWTAuthMiddleware(), middleware.SignatureMiddleware(), controllers.ShareCredentialsWithGroups) + route.POST("/share-credentials/environment", middleware.JWTAuthMiddleware(), middleware.SignatureMiddleware(), controllers.ShareCredentialsWithEnvironment) route.POST("share-folder/users", middleware.JWTAuthMiddleware(), middleware.SignatureMiddleware(), controllers.ShareFolderWithUsers) route.POST("share-folder/groups", middleware.JWTAuthMiddleware(), middleware.SignatureMiddleware(), controllers.ShareFolderWithGroups) @@ -86,4 +92,10 @@ func RegisterRoutes(route *gin.Engine) { route.PUT("/folder/:id", middleware.JWTAuthMiddleware(), controllers.EditFolder) route.PUT("/group/:id", middleware.JWTAuthMiddleware(), controllers.EditGroup) + route.GET("/environment/:name", middleware.JWTAuthMiddleware(), controllers.GetEnvironmentByName) + + route.POST("/environment/edit-field-name", middleware.JWTAuthMiddleware(), controllers.EditEnvironmentFieldName) + route.GET("/environments/:credentialId/fields", middleware.JWTAuthMiddleware(), controllers.GetCredentialEnvFieldsForEditDataSync) + route.GET("/environments/:credentialId", middleware.JWTAuthMiddleware(), controllers.GetEnvsForCredential) + } diff --git a/scripts/migrate_v011_to_v020.py b/scripts/migrate_v011_to_v020.py new file mode 100644 index 0000000..e8f765a --- /dev/null +++ b/scripts/migrate_v011_to_v020.py @@ -0,0 +1,81 @@ +import psycopg2 +from psycopg2 import sql + +# Connection parameters +dbname = "osvauld" +user = "postgres" +password = "password" +host = "host_url" +# Connect to the PostgreSQL database +conn = psycopg2.connect(dbname=dbname, user=user, password=password, host=host) + +# Create a cursor object +cur = conn.cursor() + +try: + # Fetch data from the 'fields' table + cur.execute("SELECT * FROM fields") + rows = cur.fetchall() + + # Create Data + field_groups = {} + for row in rows: + field_id, field_name, field_value, field_type, credential_id, user_id, created_at, created_by, updated_at, updated_by = row + + key = f"{field_name}_{credential_id}" + + if key not in field_groups: + + field_groups[key] = { + "field_name": field_name, + "field_type": field_type, + "credential_id": credential_id, + "created_at": created_at, + "created_by": created_by, + "updated_at": updated_at, + "updated_by": updated_by, + "user_fields": [] + } + + field_groups[key]["user_fields"].append({ + "field_value": field_value, + "user_id": user_id, + }) + + for key, value in field_groups.items(): + + # Insert data into 'field_data' + cur.execute( + sql.SQL("INSERT INTO field_data (field_name, field_type, credential_id, created_at, created_by, updated_at, updated_by) VALUES (%s, %s, %s, %s, %s, %s, %s) RETURNING id"), + (value["field_name"], value["field_type"], value["credential_id"], value["created_at"], value["created_by"], value["updated_at"], value["updated_by"]) + ) + + field_id = cur.fetchone()[0] + + for field_value in value["user_fields"]: + # Insert data into 'field_values' + cur.execute( + sql.SQL("INSERT INTO field_values (field_value, user_id, field_id) VALUES (%s, %s, %s)"), + (field_value["field_value"], field_value["user_id"], field_id) + ) + + # Change admin user type to superadmin + cur.execute("UPDATE users SET type = 'superadmin' WHERE created_at = (SELECT MIN(created_at) FROM users)") + + # Commit the transaction + conn.commit() + print("committed") + print("Data transferred successfully.") + + +except Exception as e: + print(f"An error occurred: {e}") + conn.rollback() # Roll back the transaction on error + raise e + +finally: + # Close the cursor and connection + cur.close() + conn.close() + + diff --git a/service/credential_access_service.go b/service/credential_access_service.go index 5e821a2..ad9c214 100644 --- a/service/credential_access_service.go +++ b/service/credential_access_service.go @@ -16,6 +16,7 @@ var CredentialAccessLevels = map[string]int{ "manager": 2, } +// TODO: makesure no access removal happens for superuser func GetCredentialAccessTypeForUser(ctx *gin.Context, credentialID uuid.UUID, userID uuid.UUID) (string, error) { accessRows, err := repository.GetCredentialAccessTypeForUser(ctx, db.GetCredentialAccessTypeForUserParams{ @@ -238,9 +239,17 @@ func GetCredentialUsersWithDirectAccess(ctx *gin.Context, credentialID uuid.UUID if err != nil { return nil, err } + superUser, err := repository.GetSuperUser(ctx) + if err != nil { + return nil, err + + } userAccessObjs := []dto.CredentialUserWithAccess{} for _, access := range userAccess { + if access.UserID == superUser.ID { + continue + } userAccessObjs = append(userAccessObjs, dto.CredentialUserWithAccess{ UserID: access.UserID, Name: access.Name, diff --git a/service/credential_service.go b/service/credential_service.go index 799c81c..488c2b3 100644 --- a/service/credential_service.go +++ b/service/credential_service.go @@ -39,7 +39,7 @@ func AddCredential(ctx *gin.Context, request dto.AddCredentialRequest, caller uu FolderID: request.FolderID, CredentialType: request.CredentialType, CreatedBy: caller, - UserFields: request.UserFields, + Fields: request.Fields, CredentialAccessArgs: credentialAccessRecords, Domain: request.Domain, } @@ -63,8 +63,8 @@ func GetCredentialDataByID(ctx *gin.Context, credentialID uuid.UUID, caller uuid } fields, err := repository.GetNonSensitiveFieldsForCredentialIDs(ctx, db.GetNonSensitiveFieldsForCredentialIDsParams{ - Credentials: []uuid.UUID{credentialID}, - UserID: caller, + Credentialids: []uuid.UUID{credentialID}, + UserID: caller, }) if err != nil { return dto.CredentialForUser{}, err @@ -144,8 +144,8 @@ func GetCredentialsByFolder(ctx *gin.Context, folderID uuid.UUID, userID uuid.UU } FieldsData, err := repository.GetNonSensitiveFieldsForCredentialIDs(ctx, db.GetNonSensitiveFieldsForCredentialIDsParams{ - Credentials: credentialIDs, - UserID: userID, + Credentialids: credentialIDs, + UserID: userID, }) if err != nil { return []dto.CredentialForUser{}, err @@ -196,8 +196,8 @@ func GetCredentialsByIDs(ctx *gin.Context, credentialIDs []uuid.UUID, userID uui } FieldsData, err := repository.GetNonSensitiveFieldsForCredentialIDs(ctx, db.GetNonSensitiveFieldsForCredentialIDsParams{ - Credentials: credentialIDs, - UserID: userID, + Credentialids: credentialIDs, + UserID: userID, }) if err != nil { return nil, err @@ -254,15 +254,7 @@ func EditCredential(ctx *gin.Context, credentialID uuid.UUID, request dto.EditCr return err } - err := repository.EditCredential(ctx, db.EditCredentialTransactionParams{ - CredentialID: credentialID, - Name: request.Name, - Description: sql.NullString{String: request.Description, Valid: true}, - CredentialType: request.CredentialType, - UserFields: request.UserFields, - EditedBy: caller, - }) - + err := repository.EditCredential(ctx, request, caller) if err != nil { return err } diff --git a/service/envrionment_service.go b/service/envrionment_service.go new file mode 100644 index 0000000..86a7edd --- /dev/null +++ b/service/envrionment_service.go @@ -0,0 +1,126 @@ +package service + +import ( + "osvauld/customerrors" + db "osvauld/db/sqlc" + dto "osvauld/dtos" + "osvauld/repository" + + "github.com/gin-gonic/gin" + "github.com/google/uuid" +) + +func VerifyEnvironmentAccessForUser(ctx *gin.Context, environmentID uuid.UUID, userID uuid.UUID) error { + + hasAccess, err := repository.IsEnvironmentOwner(ctx, db.IsEnvironmentOwnerParams{ + ID: environmentID, + CreatedBy: userID, + }) + if err != nil { + return err + } + if !hasAccess { + return &customerrors.UserDoesNotHaveEnvironmentAccess{UserID: userID, EnvironmentID: environmentID} + } + + return nil +} + +func GetEnvironmentFields(ctx *gin.Context, envID uuid.UUID) ([]dto.CredentialEnvFields, error) { + envFields, err := repository.GetEnvironmentFields(ctx, envID) + credentialFieldMap := make(map[uuid.UUID][]dto.EnvFieldData) + credentialIDNameMap := make(map[uuid.UUID]string) + for _, field := range envFields { + credentialFieldMap[field.CredentialID] = append(credentialFieldMap[field.CredentialID], dto.EnvFieldData{ + FieldID: field.ID, + FieldName: field.FieldName, + FieldValue: field.FieldValue, + }) + credentialIDNameMap[field.CredentialID] = field.CredentialName + } + + if err != nil { + return []dto.CredentialEnvFields{}, err + } + + var credentialEnvData = []dto.CredentialEnvFields{} + + for credentialID, fields := range credentialFieldMap { + credentialEnvData = append(credentialEnvData, dto.CredentialEnvFields{ + CredentialID: credentialID, + CredentialName: credentialIDNameMap[credentialID], + Fields: fields, + }) + } + return credentialEnvData, nil +} + +func GetEnvironmentByName(ctx *gin.Context, environmentName string, userID uuid.UUID) ([]db.GetEnvironmentFieldsByNameRow, error) { + // TODO: validation + environment, err := repository.GetEnvironmentFieldsByName(ctx, environmentName) + if err != nil { + return []db.GetEnvironmentFieldsByNameRow{}, err + } + return environment, nil +} + +func AddEnvironment(ctx *gin.Context, environment dto.AddEnvironment, caller uuid.UUID) (uuid.UUID, error) { + // TODO: verify no duplicate name for user + return repository.AddEnvironment(ctx, db.AddEnvironmentParams{ + Name: environment.Name, + CliUser: environment.CliUser, + CreatedBy: caller, + }) +} + +func GetEnvironments(ctx *gin.Context, userID uuid.UUID) ([]db.GetEnvironmentsForUserRow, error) { + return repository.GetEnvironments(ctx, userID) +} + +func EditEnvFieldName(ctx *gin.Context, payload dto.EditEnvFieldName, caller uuid.UUID) (map[string]string, error) { + + if err := VerifyEnvironmentAccessForUser(ctx, payload.EnvironmentID, caller); err != nil { + return nil, err + } + + fieldName, err := repository.EditEnvironmentFieldName(ctx, db.EditEnvironmentFieldNameByIDParams{ + ID: payload.FieldID, + FieldName: payload.FieldName, + EnvID: payload.EnvironmentID, + }) + if err != nil { + return nil, err + } + + return map[string]string{"fieldName": fieldName}, nil +} + +func GetCredentialEnvFieldsForEditDataSync(ctx *gin.Context, credentialID uuid.UUID, caller uuid.UUID) (map[uuid.UUID][]dto.CredentialEnvFieldsForEditDataSync, error) { + // TODO: validation needs to change + + // if err := VerifyEnvironmentAccessForUser(ctx, credentialID, caller); err != nil { + // return nil, err + // } + + dataRows, err := repository.GetEnvFieldsForCredential(ctx, credentialID) + if err != nil { + return nil, err + } + fieldIDEnvFieldMap := make(map[uuid.UUID][]dto.CredentialEnvFieldsForEditDataSync) + for _, row := range dataRows { + fieldIDEnvFieldMap[row.Fieldid] = append(fieldIDEnvFieldMap[row.Fieldid], dto.CredentialEnvFieldsForEditDataSync{ + EnvID: row.EnvID, + EnvFieldID: row.Envfieldid, + CliUserID: row.Userid, + CliUserPublicKey: row.PublicKey.String, + }) + } + + return fieldIDEnvFieldMap, nil + +} + +func GetEnvsForCredential(ctx *gin.Context, credentialID uuid.UUID) ([]db.GetEnvForCredentialRow, error) { + // TODO: add validations for fetching envs for credential + return repository.GetEnvForCredential(ctx, credentialID) +} diff --git a/service/field_service.go b/service/field_service.go index 477f552..144c33d 100644 --- a/service/field_service.go +++ b/service/field_service.go @@ -27,7 +27,7 @@ func GetSensitiveFields(ctx *gin.Context, credentialID uuid.UUID, caller uuid.UU ID: field.ID, FieldName: field.FieldName, FieldValue: field.FieldValue, - FieldType: "sensitive", + FieldType: field.FieldType, }) } diff --git a/service/folder_access_service.go b/service/folder_access_service.go index 4e73846..170cf42 100644 --- a/service/folder_access_service.go +++ b/service/folder_access_service.go @@ -168,9 +168,17 @@ func GetFolderUsersWithDirectAccess(ctx *gin.Context, folderID uuid.UUID, caller if err != nil { return nil, err } + superUser, err := repository.GetSuperUser(ctx) + if err != nil { + return []dto.FolderUserWithAccess{}, err + } userAccessObjs := []dto.FolderUserWithAccess{} for _, access := range userAccess { + // Do not include super user in the list + if access.UserID == superUser.ID { + continue + } userAccessObjs = append(userAccessObjs, dto.FolderUserWithAccess{ UserID: access.UserID, Name: access.Name, diff --git a/service/folder_service.go b/service/folder_service.go index 2735148..27ad814 100644 --- a/service/folder_service.go +++ b/service/folder_service.go @@ -23,6 +23,13 @@ func CreateFolder(ctx *gin.Context, folder dto.CreateFolderRequest, caller uuid. Description: sql.NullString{String: folder.Description, Valid: true}, CreatedBy: caller, } + if folder.SharedFolder { + user, err := repository.GetSuperUser(ctx) + if err != nil { + return dto.FolderDetails{}, err + } + createFolderParams.SuperUser = &user.ID + } folderDetails, err := repository.CreateFolder(ctx, createFolderParams) if err != nil { diff --git a/service/group_service.go b/service/group_service.go index 5910214..850c2ee 100644 --- a/service/group_service.go +++ b/service/group_service.go @@ -125,10 +125,10 @@ func AddMemberToGroup(ctx *gin.Context, payload dto.AddMemberToGroupRequest, cal return err } - userFieldRecords := []db.AddFieldParams{} + userFieldRecords := []db.AddFieldValueParams{} for _, credential := range payload.Credentials { - fieldDataExists, err := repository.CheckFieldEntryExists(ctx, db.CheckFieldEntryExistsParams{ + fieldDataExists, err := repository.CheckAnyCredentialAccessEntryExists(ctx, db.CheckAnyCredentialAccessEntryExistsParams{ UserID: payload.MemberID, CredentialID: credential.CredentialID, }) @@ -152,6 +152,17 @@ func AddMemberToGroup(ctx *gin.Context, payload dto.AddMemberToGroupRequest, cal credentialAccessRecords := []db.AddCredentialAccessParams{} for _, credentialDetails := range credentialIDAndTypeWithGroupAccess { + accessEntry, err := repository.CheckCredentialAccessEntryExists(ctx, db.CheckCredentialAccessEntryExistsParams{ + UserID: payload.MemberID, + CredentialID: credentialDetails.CredentialID, + GroupID: uuid.NullUUID{UUID: payload.GroupID, Valid: true}, + }) + if err != nil { + return err + } + if accessEntry { + continue + } credentialAccessRecord := db.AddCredentialAccessParams{ CredentialID: credentialDetails.CredentialID, UserID: payload.MemberID, diff --git a/service/share_credential_service.go b/service/share_credential_service.go index 8d31278..0b4a6b2 100644 --- a/service/share_credential_service.go +++ b/service/share_credential_service.go @@ -10,32 +10,15 @@ import ( "github.com/google/uuid" ) -func CreateFieldDataRecords(ctx *gin.Context, credential dto.ShareCredentialPayload, userID uuid.UUID, caller uuid.UUID) ([]db.AddFieldParams, error) { - - fieldData, err := repository.GetAllFieldsForCredentialIDs(ctx, db.GetAllFieldsForCredentialIDsParams{ - UserID: caller, - Credentials: []uuid.UUID{credential.CredentialID}, - }) - if err != nil { - return nil, err - } - - fieldMap := make(map[uuid.UUID]db.GetAllFieldsForCredentialIDsRow) - for _, field := range fieldData { - fieldMap[field.ID] = field - } - - userFieldRecords := []db.AddFieldParams{} +func CreateFieldDataRecords(ctx *gin.Context, credential dto.ShareCredentialPayload, userID uuid.UUID, caller uuid.UUID) ([]db.AddFieldValueParams, error) { + userFieldRecords := []db.AddFieldValueParams{} for _, field := range credential.Fields { - userFieldRecord := db.AddFieldParams{ - FieldName: fieldMap[field.ID].FieldName, - FieldType: fieldMap[field.ID].FieldType, - FieldValue: field.FieldValue, - UserID: userID, - CredentialID: credential.CredentialID, - CreatedBy: uuid.NullUUID{UUID: caller, Valid: true}, + userFieldRecord := db.AddFieldValueParams{ + FieldID: field.ID, + FieldValue: field.FieldValue, + UserID: userID, } userFieldRecords = append(userFieldRecords, userFieldRecord) @@ -62,7 +45,7 @@ func ShareCredentialsWithUsers(ctx *gin.Context, payload []dto.ShareCredentialsF // we share all the credentials for a single user in a single transaction for _, userData := range payload { - userFieldRecords := []db.AddFieldParams{} + userFieldRecords := []db.AddFieldValueParams{} credentialAccessRecords := []db.AddCredentialAccessParams{} userShareResponse := ShareCredentialsWithUserResponse{ UserID: userData.UserID, @@ -87,7 +70,6 @@ func ShareCredentialsWithUsers(ctx *gin.Context, payload []dto.ShareCredentialsF return nil, err } if exists { - logger.Infof("Credential %s already shared for user %s", credential.CredentialID, userData.UserID) continue } else { @@ -100,10 +82,7 @@ func ShareCredentialsWithUsers(ctx *gin.Context, payload []dto.ShareCredentialsF } - /////////////////////////////////////////////////////////////////////////////////////////// - // Check Fields already shared for user - - fieldDataExists, err := repository.CheckFieldEntryExists(ctx, db.CheckFieldEntryExistsParams{ + fieldExists, err := repository.CheckAnyCredentialAccessEntryExists(ctx, db.CheckAnyCredentialAccessEntryExistsParams{ UserID: userData.UserID, CredentialID: credential.CredentialID, }) @@ -111,17 +90,15 @@ func ShareCredentialsWithUsers(ctx *gin.Context, payload []dto.ShareCredentialsF return nil, err } - if !fieldDataExists { + if !fieldExists { userFields, err := CreateFieldDataRecords(ctx, credential, userData.UserID, caller) + if err != nil { return nil, err } userFieldRecords = append(userFieldRecords, userFields...) - } else { - logger.Infof("Field data already exists for credential %s and user %s", credential.CredentialID, userData.UserID) } - ////////////////////////////////////////////////////////////////////////////////////////////// } @@ -157,7 +134,7 @@ func ShareCredentialsWithGroups(ctx *gin.Context, payload []dto.CredentialsForGr var responses []ShareCredentialsWithGroupResponse for _, groupData := range payload { - groupFieldRecords := []db.AddFieldParams{} + groupFieldRecords := []db.AddFieldValueParams{} credentialAccessRecords := []db.AddCredentialAccessParams{} for _, userData := range groupData.UserData { @@ -195,8 +172,8 @@ func ShareCredentialsWithGroups(ctx *gin.Context, payload []dto.CredentialsForGr credentialAccessRecords = append(credentialAccessRecords, credentialAccessRecord) } - - fieldDataExists, err := repository.CheckFieldEntryExists(ctx, db.CheckFieldEntryExistsParams{ + //if access entry exists dont need to add field + fieldDataExists, err := repository.CheckAnyCredentialAccessEntryExists(ctx, db.CheckAnyCredentialAccessEntryExistsParams{ UserID: userData.UserID, CredentialID: credential.CredentialID, }) @@ -248,20 +225,18 @@ type ShareFolderWithUserResponse struct { func ShareFolderWithUsers(ctx *gin.Context, payload dto.ShareFolderWithUsersRequest, caller uuid.UUID) ([]ShareFolderWithUserResponse, error) { - err := VerifyFolderManageAccessForUser(ctx, payload.FolderID, caller) - if err != nil { - return nil, err - } - + err := VerifyFolderManageAccessForUser(ctx, payload.FolderID, caller) + if err != nil { + return nil, err + } // the following loop is for grouping the credentials shared for a single user // so that we can share all the credentials for a single user in a single transaction var responses []ShareFolderWithUserResponse for _, userData := range payload.UserData { - credentialAccessRecords := []db.AddCredentialAccessParams{} - userFieldRecords := []db.AddFieldParams{} + userFieldRecords := []db.AddFieldValueParams{} folderAccessRecords := []db.AddFolderAccessParams{} userShareResponse := ShareFolderWithUserResponse{} @@ -277,7 +252,8 @@ func ShareFolderWithUsers(ctx *gin.Context, payload dto.ShareFolderWithUsersRequ return nil, err } - fieldDataExists, err := repository.CheckFieldEntryExists(ctx, db.CheckFieldEntryExistsParams{ + logger.Infof("Sharing credential %s with user %s", credential.CredentialID, userData.UserID) + fieldDataExists, err := repository.CheckAnyCredentialAccessEntryExists(ctx, db.CheckAnyCredentialAccessEntryExistsParams{ UserID: userData.UserID, CredentialID: credential.CredentialID, }) @@ -377,7 +353,7 @@ func ShareFolderWithGroups(ctx *gin.Context, payload dto.ShareFolderWithGroupsRe var responses []ShareFolderWithGroupResponse for _, groupData := range payload.GroupData { - groupFieldRecords := []db.AddFieldParams{} + groupFieldRecords := []db.AddFieldValueParams{} credentialAccessRecords := []db.AddCredentialAccessParams{} folderAccessRecords := []db.AddFolderAccessParams{} @@ -393,7 +369,7 @@ func ShareFolderWithGroups(ctx *gin.Context, payload dto.ShareFolderWithGroupsRe return nil, err } - fieldDataExists, err := repository.CheckFieldEntryExists(ctx, db.CheckFieldEntryExistsParams{ + fieldDataExists, err := repository.CheckAnyCredentialAccessEntryExists(ctx, db.CheckAnyCredentialAccessEntryExistsParams{ UserID: userData.UserID, CredentialID: credential.CredentialID, }) @@ -484,3 +460,62 @@ func ShareFolderWithGroups(ctx *gin.Context, payload dto.ShareFolderWithGroupsRe return responses, nil } + +func ShareCredentialsWithEnvironment(ctx *gin.Context, payload dto.ShareCredentialsWithEnvironmentRequest, caller uuid.UUID) error { + var credentialEnvDataList []dto.CredentialEnvData + var credentialIDs []uuid.UUID + for _, credentialData := range payload.Credentials { + credentialIDs = append(credentialIDs, credentialData.CredentialID) + } + credentialFields, err := repository.GetAllFieldsForCredentialIDs(ctx, db.GetAllFieldsForCredentialIDsParams{ + UserID: caller, + Credentials: credentialIDs, + }) + if err != nil { + return err + } + + fieldIDs := []uuid.UUID{} + fieldMap := make(map[uuid.UUID]db.GetAllFieldsForCredentialIDsRow) + for _, field := range credentialFields { + fieldMap[field.ID] = field + fieldIDs = append(fieldIDs, field.ID) + } + + fieldValues, err := repository.GetFieldValueIDsForFieldIDs(ctx, db.GetFieldValueIDsForFieldIDsParams{ + UserID: caller, + Fieldids: fieldIDs, + }) + if err != nil { + return err + } + + fieldIDfieldValueIDMap := make(map[uuid.UUID]uuid.UUID) + for _, fieldValue := range fieldValues { + fieldIDfieldValueIDMap[fieldValue.FieldID] = fieldValue.ID + } + + for _, credential := range payload.Credentials { + + exists, err := repository.CheckCredentialExistsInEnvironment(ctx, credential.CredentialID, payload.EnvId) + if err != nil { + return err + } + if exists { + logger.Infof("Credential %s already shared for environment %s", credential.CredentialID, payload.EnvId) + continue + } + for _, field := range credential.Fields { + credentialEnvData := dto.CredentialEnvData{ + CredentialID: credential.CredentialID, + FieldValue: field.FieldValue, + FieldName: fieldMap[field.FieldID].FieldName, + ParentFieldValueID: fieldIDfieldValueIDMap[field.FieldID], + EnvID: payload.EnvId, + } + credentialEnvDataList = append(credentialEnvDataList, credentialEnvData) + } + } + repository.AddCredentialFieldsToEnvironment(ctx, credentialEnvDataList) + return nil +} diff --git a/service/user_service.go b/service/user_service.go index 4336794..0d52b73 100644 --- a/service/user_service.go +++ b/service/user_service.go @@ -194,3 +194,11 @@ func GetAllUsers(ctx *gin.Context) ([]db.GetAllUsersRow, error) { func GetUserDeviceKey(ctx *gin.Context, userID uuid.UUID) (string, error) { return repository.GetUserDeviceKey(ctx, userID) } + +func CreateCLIUser(ctx *gin.Context, user dto.CreateCLIUser, caller uuid.UUID) (uuid.UUID, error) { + return repository.CreateCLIUser(ctx, user, caller) +} + +func GetCliUsers(ctx *gin.Context, caller uuid.UUID) ([]db.GetCliUsersRow, error) { + return repository.GetCliUsers(ctx, caller) +}