Skip to content

Commit

Permalink
Credentials CRUD
Browse files Browse the repository at this point in the history
  • Loading branch information
salilponde committed Dec 18, 2023
1 parent 8c078fe commit 38b598c
Show file tree
Hide file tree
Showing 27 changed files with 1,380 additions and 10 deletions.
4 changes: 3 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ go 1.21
require (
github.com/GeertJohan/go.rice v1.0.3
github.com/docker/docker v24.0.7+incompatible
github.com/gabemarshall/pty v0.0.0-20220927143247-d84f0bb0c17e
github.com/glebarez/sqlite v1.10.0
github.com/go-playground/validator/v10 v10.14.0
github.com/google/go-github/v57 v57.0.0
github.com/google/uuid v1.4.0
github.com/gorilla/websocket v1.5.1
github.com/labstack/echo/v4 v4.11.3
Expand All @@ -24,13 +26,13 @@ require (
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/gabemarshall/pty v0.0.0-20220927143247-d84f0bb0c17e // indirect
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
github.com/glebarez/go-sqlite v1.21.2 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/leodido/go-urn v1.2.4 // indirect
Expand Down
11 changes: 7 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migc
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0=
github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/daaku/go.zipexe v1.0.2 h1:Zg55YLYTr7M9wjKn8SY/WcpuuEi+kR2u4E8RhvpyXmk=
github.com/daaku/go.zipexe v1.0.2/go.mod h1:5xWogtqlYnfBXkSB1o9xysukNP9GTvaNkqzUZbt3Bw8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
Expand Down Expand Up @@ -47,8 +45,13 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-github/v57 v57.0.0 h1:L+Y3UPTY8ALM8x+TV0lg+IEBI+upibemtBD8Q9u7zHs=
github.com/google/go-github/v57 v57.0.0/go.mod h1:s0omdnye0hvK/ecLvpsGfJMiRt85PimQh4oygmLIxHw=
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
Expand Down
189 changes: 189 additions & 0 deletions pkg/server/handler/credential.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
package handler

import (
"strconv"

"github.com/productiveops/dokemon/pkg/server/model"

"github.com/labstack/echo/v4"
)

func (h *Handler) CreateCredential(c echo.Context) error {
m := model.Credential{}
r := &credentialCreateRequest{}
if err := r.bind(c, &m); err != nil {
return unprocessableEntity(c, err)
}

isUnique, err := h.credentialStore.IsUniqueName(r.Name)
if err != nil {
panic(err)
}

if !isUnique {
return unprocessableEntity(c, duplicateNameError())
}

if err := h.credentialStore.Create(&m); err != nil {
panic(err)
}

return created(c, m.Id)
}

func (h *Handler) UpdateCredentialDetails(c echo.Context) error {
id, err := strconv.Atoi(c.Param("id"))
if err != nil {
return unprocessableEntity(c, routeIntExpectedError("id"))
}

m, err := h.credentialStore.GetById(uint(id))
if err != nil {
panic(err)
}

if m == nil {
return resourceNotFound(c, "Credential")
}

r := &credentialUpdateDetailsRequest{Id: uint(id)}
if err := r.bind(c, m); err != nil {
return unprocessableEntity(c, err)
}

isUnique, err := h.credentialStore.IsUniqueNameExcludeItself(r.Name, r.Id)
if err != nil {
panic(err)
}

if !isUnique {
return unprocessableEntity(c, duplicateNameError())
}

if err := h.credentialStore.Update(m); err != nil {
panic(err)
}

return noContent(c)
}

func (h *Handler) UpdateCredentialSecret(c echo.Context) error {
id, err := strconv.Atoi(c.Param("id"))
if err != nil {
return unprocessableEntity(c, routeIntExpectedError("id"))
}

m, err := h.credentialStore.GetById(uint(id))
if err != nil {
panic(err)
}

if m == nil {
return resourceNotFound(c, "Credential")
}

r := &credentialUpdateSecretRequest{Id: uint(id)}
if err := r.bind(c, m); err != nil {
return unprocessableEntity(c, err)
}

if err := h.credentialStore.Update(m); err != nil {
panic(err)
}

return noContent(c)
}

func (h *Handler) DeleteCredentialById(c echo.Context) error {
id, err := strconv.Atoi(c.Param("id"))
if err != nil {
return unprocessableEntity(c, routeIntExpectedError("id"))
}

exists, err := h.credentialStore.Exists(uint(id))
if err != nil {
panic(err)
}

if !exists {
return resourceNotFound(c, "Credential")
}

if err := h.credentialStore.DeleteById(uint(id)); err != nil {
panic(err)
}

return noContent(c)
}

func (h *Handler) GetCredentialList(c echo.Context) error {
p, err := strconv.Atoi(c.QueryParam("p"))
if err != nil {
return unprocessableEntity(c, routeIntExpectedError("p"))
}

if p < 1 {
return unprocessableEntity(c, queryGte1ExpectedError("p"))
}

s, err := strconv.Atoi(c.QueryParam("s"))
if err != nil {
return unprocessableEntity(c, routeIntExpectedError("s"))
}

if s < 1 {
return unprocessableEntity(c, queryGte1ExpectedError("s"))
}

rows, totalRows, err := h.credentialStore.GetList(uint(p), uint(s))
if err != nil {
panic(err)
}

return ok(c, newPageResponse[credentialHead](newCredentialHeadList(rows), uint(p), uint(s), uint(totalRows)))
}

func (h *Handler) GetCredentialById(c echo.Context) error {
id, err := strconv.Atoi(c.Param("id"))
if err != nil {
return unprocessableEntity(c, routeIntExpectedError("id"))
}

m, err := h.credentialStore.GetById(uint(id))
if err != nil {
panic(err)
}

if m == nil {
return resourceNotFound(c, "Credential")
}

return ok(c, newCredentialResponse(m))
}

func (h *Handler) IsUniqueCredentialName(c echo.Context) error {
value := c.QueryParam("value")

unique, err := h.credentialStore.IsUniqueName(value)
if err != nil {
panic(err)
}

return ok(c, newUniqueResponse(unique))
}

func (h *Handler) IsUniqueCredentialNameExcludeItself(c echo.Context) error {
id, err := strconv.Atoi(c.Param("id"))
if err != nil {
return unprocessableEntity(c, routeIntExpectedError("id"))
}

value := c.QueryParam("value")

unique, err := h.credentialStore.IsUniqueNameExcludeItself(value, uint(id))
if err != nil {
panic(err)
}

return ok(c, newUniqueResponse(unique))
}
82 changes: 82 additions & 0 deletions pkg/server/handler/github.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package handler

import (
"context"
"errors"
"strings"

"github.com/google/go-github/v57/github"
"github.com/labstack/echo/v4"
"github.com/productiveops/dokemon/pkg/crypto/ske"
"github.com/rs/zerolog/log"
)

type gitHubUrlParts struct {
Owner string
Repo string
Ref string
Path string
}

func getGitHubUrlParts(url string) (*gitHubUrlParts, error) {
ret := &gitHubUrlParts{}

if !strings.HasPrefix(url, "https://github.com") {
return nil, errors.New("URL should begin with https://github.com")
}

parts := strings.Split(url[19:], "/")
if len(parts) < 5 {
return nil, errors.New("URL should be of format: https://github.com/OWNER/REPO/blob/REF/path/to/filename.extension")
}

ret.Owner = parts[0]
ret.Repo = parts[1]
ret.Ref = parts[3]
ret.Path = strings.Join(parts[4:], "/")

return ret, nil
}

func (h *Handler) RetrieveGitHubFileContent(c echo.Context) error {
r := &gitHubfileContentRetrieveRequest{}
if err := r.bind(c); err != nil {
return unprocessableEntity(c, err)
}

client := github.NewClient(nil)
if r.CredentialId != nil {
credential, err := h.credentialStore.GetById(*r.CredentialId)
if err != nil {
return unprocessableEntity(c, errors.New("Credentials not found"))
}

decryptedSecret, err := ske.Decrypt(credential.Secret)
if err != nil {
panic(err)
}
client = github.NewClient(nil).WithAuthToken(decryptedSecret)
}

p, err := getGitHubUrlParts(r.Url)
if err != nil {
return unprocessableEntity(c, err)
}

content, _, _, err := client.Repositories.GetContents(context.Background(), p.Owner, p.Repo, p.Path, &github.RepositoryContentGetOptions{Ref: p.Ref})
if err != nil {
if r.CredentialId != nil {
log.Error().Err(err).Str("url", r.Url).Uint("credentialId", *r.CredentialId).Msg("Error while retriveing file content from GitHub")
} else {
log.Error().Err(err).Str("url", r.Url).Msg("Error while retriveing file content from GitHub")
}
return unprocessableEntity(c, errors.New("Error while retrieving file content from provided GitHub URL"))
}

text, err := content.GetContent()
if err != nil {
panic(err)
}

return ok(c, newGitHubfileContentResponse(&text))
}
16 changes: 16 additions & 0 deletions pkg/server/handler/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (

type Handler struct {
composeProjectsPath string
credentialStore store.CredentialStore
environmentStore store.EnvironmentStore
userStore store.UserStore
nodeStore store.NodeStore
Expand All @@ -29,6 +30,7 @@ var (

func NewHandler(
composeProjectsPath string,
credentialStore store.CredentialStore,
environmentStore store.EnvironmentStore,
userStore store.UserStore,
nodeStore store.NodeStore,
Expand All @@ -40,6 +42,7 @@ func NewHandler(
) *Handler {
return &Handler{
composeProjectsPath: composeProjectsPath,
credentialStore: credentialStore,
environmentStore: environmentStore,
userStore: userStore,
nodeStore: nodeStore,
Expand All @@ -60,10 +63,23 @@ func (h *Handler) Register(e *echo.Echo) {

v1 := e.Group("/api/v1")

github := v1.Group("/github/filecontent/load")
github.POST("", h.RetrieveGitHubFileContent)

settings := v1.Group("/settings")
settings.GET("/:id", h.GetSettingById)
settings.PUT("/:id", h.UpdateSetting)

credentials := v1.Group("/credentials")
credentials.POST("", h.CreateCredential)
credentials.PUT("/:id", h.UpdateCredentialDetails)
credentials.PUT("/:id/secret", h.UpdateCredentialSecret)
credentials.GET("", h.GetCredentialList)
credentials.GET("/:id", h.GetCredentialById)
credentials.DELETE("/:id", h.DeleteCredentialById)
credentials.GET("/uniquename", h.IsUniqueCredentialName)
credentials.GET("/:id/uniquename", h.IsUniqueCredentialNameExcludeItself)

environments := v1.Group("/environments")
environments.POST("", h.CreateEnvironment)
environments.PUT("/:id", h.UpdateEnvironment)
Expand Down
Loading

0 comments on commit 38b598c

Please sign in to comment.