diff --git a/db/AccessKey.go b/db/AccessKey.go index b4eeb1f38..6b11cf314 100644 --- a/db/AccessKey.go +++ b/db/AccessKey.go @@ -43,6 +43,11 @@ type AccessKey struct { LoginPassword LoginPassword `db:"-" json:"login_password"` SshKey SshKey `db:"-" json:"ssh"` OverrideSecret bool `db:"-" json:"override_secret"` + + RepositorySSHKeys []*Repository `gorm:"foreignKey:SSHKeyID" json:"-"` + InventorySSHKeys []*Inventory `gorm:"foreignKey:SSHKeyID" json:"-"` + InventoryBecomeKeys []*Inventory `gorm:"foreignKey:BecomeKeyID" json:"-"` + TemplateVaultKeys []*Template `gorm:"foreignKey:VaultKeyID" json:"-"` } type LoginPassword struct { diff --git a/db/params.go b/db/params.go new file mode 100644 index 000000000..df8ad0070 --- /dev/null +++ b/db/params.go @@ -0,0 +1,121 @@ +package db + +type EventParams struct { + UserID int + ProjectID int + Query RetrieveQueryParams +} + +type UserParams struct { + UserID int + Password string + Query RetrieveQueryParams +} + +type TokenParams struct { + UserID int + TokenID string +} + +type SessionParams struct { + UserID int + SessionID string +} + +type RunnerParams struct { + RunnerID int + ProjectID int +} + +type ProjectParams struct { + Admin bool + UserID int + ProjectID int +} + +type MemberParams struct { + ProjectID int + UserID int + Query RetrieveQueryParams +} + +type TemplateParams struct { + ProjectID int + TemplateID int + Filter TemplateFilter + Query RetrieveQueryParams +} + +type AccessKeyParams struct { + ProjectID int + AccessKeyID int + OldKey string + Query RetrieveQueryParams +} + +type EnvParams struct { + ProjectID int + EnvironmentID int + Query RetrieveQueryParams +} + +type InventoryParams struct { + ProjectID int + InventoryID int + Query RetrieveQueryParams +} + +type RepoParams struct { + ProjectID int + RepositoryID int + Query RetrieveQueryParams +} + +type ViewParams struct { + ProjectID int + ViewID int + Positions map[int]int + Query RetrieveQueryParams +} + +type ScheduleParams struct { + ProjectID int + ScheduleID int + TemplateID int + Hash string + Query RetrieveQueryParams +} + +type TaskParams struct { + ProjectID int + TaskID int + TemplateID int + Query RetrieveQueryParams +} + +type IntegrationParams struct { + ProjectID int + IntegrationID int + Alias string + Query RetrieveQueryParams +} + +type IntegrationExtractValueParams struct { + ProjectID int + IntegrationID int + ValueID int + Query RetrieveQueryParams +} + +type IntegrationMatcherParams struct { + ProjectID int + IntegrationID int + MatcherID int + Query RetrieveQueryParams +} + +type IntegrationAliasParams struct { + ProjectID int + IntegrationID int + AliasID int +} diff --git a/go.mod b/go.mod index 64b4da012..474da42c1 100644 --- a/go.mod +++ b/go.mod @@ -4,9 +4,12 @@ go 1.21 require ( github.com/Masterminds/squirrel v1.5.4 + github.com/asdine/storm/v3 v3.2.1 github.com/coreos/go-oidc/v3 v3.9.0 github.com/creack/pty v1.1.21 + github.com/glebarez/sqlite v1.11.0 github.com/go-git/go-git/v5 v5.11.0 + github.com/go-gormigrate/gormigrate/v2 v2.1.2 github.com/go-gorp/gorp/v3 v3.1.0 github.com/go-ldap/ldap/v3 v3.4.6 github.com/go-sql-driver/mysql v1.7.1 @@ -17,6 +20,7 @@ require ( github.com/gorilla/securecookie v1.1.2 github.com/gorilla/websocket v1.5.1 github.com/lib/pq v1.10.9 + github.com/pkg/errors v0.9.1 github.com/robfig/cron/v3 v3.0.1 github.com/sirupsen/logrus v1.9.3 github.com/snikch/goodman v0.0.0-20171125024755-10e37e294daa @@ -25,6 +29,9 @@ require ( go.etcd.io/bbolt v1.3.9 golang.org/x/crypto v0.21.0 golang.org/x/oauth2 v0.17.0 + gorm.io/driver/mysql v1.5.6 + gorm.io/driver/postgres v1.5.7 + gorm.io/gorm v1.25.10 ) require ( @@ -34,8 +41,10 @@ require ( github.com/ProtonMail/go-crypto v1.0.0 // indirect github.com/cloudflare/circl v1.3.7 // indirect github.com/cyphar/filepath-securejoin v0.2.4 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect github.com/emirpasic/gods v1.18.1 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/glebarez/go-sqlite v1.21.2 // indirect github.com/go-asn1-ber/asn1-ber v1.5.5 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-git/go-billy/v5 v5.5.0 // indirect @@ -45,11 +54,18 @@ require ( github.com/google/go-querystring v1.1.0 // indirect github.com/google/uuid v1.6.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect + github.com/jackc/pgx/v5 v5.4.3 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jinzhu/now v1.1.5 // indirect github.com/kevinburke/ssh_config v1.2.0 // indirect github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect + github.com/mattn/go-isatty v0.0.17 // indirect github.com/pjbgf/sha1cd v0.3.0 // indirect + github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/rogpeppe/go-internal v1.12.0 // indirect github.com/sergi/go-diff v1.3.1 // indirect github.com/skeema/knownhosts v1.2.1 // indirect @@ -58,8 +74,13 @@ require ( golang.org/x/mod v0.15.0 // indirect golang.org/x/net v0.23.0 // indirect golang.org/x/sys v0.18.0 // indirect + golang.org/x/text v0.14.0 // indirect golang.org/x/tools v0.18.0 // indirect google.golang.org/appengine v1.6.8 // indirect google.golang.org/protobuf v1.33.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect + modernc.org/libc v1.22.5 // indirect + modernc.org/mathutil v1.5.0 // indirect + modernc.org/memory v1.5.0 // indirect + modernc.org/sqlite v1.23.1 // indirect ) diff --git a/go.sum b/go.sum index 3994d751e..51a0f3254 100644 --- a/go.sum +++ b/go.sum @@ -2,6 +2,8 @@ dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8= github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= +github.com/DataDog/zstd v1.4.1 h1:3oxKN3wbHibqx897utPC2LTQU4J+IHWWJO+glkAkpFM= +github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= github.com/Masterminds/squirrel v1.5.4 h1:uUcX/aBc8O7Fg9kaISIUsHXdKuqehiXAMQTYX8afzqM= github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= @@ -9,12 +11,16 @@ 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/ProtonMail/go-crypto v1.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0kC2U78= github.com/ProtonMail/go-crypto v1.0.0/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= +github.com/Sereal/Sereal v0.0.0-20190618215532-0b8ac451a863 h1:BRrxwOZBolJN4gIwvZMJY1tzqBvQgpaZiQRuIDD40jM= +github.com/Sereal/Sereal v0.0.0-20190618215532-0b8ac451a863/go.mod h1:D0JMgToj/WdxCgd30Kc1UcA9E+WdZoJqeVOuYW7iTBM= github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74 h1:Kk6a4nehpJ3UuJRqlA3JxYxBZEqCeOmATOvrbT4p9RA= github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/asdine/storm/v3 v3.2.1 h1:I5AqhkPK6nBZ/qJXySdI7ot5BlXSZ7qvDY1zAn5ZJac= +github.com/asdine/storm/v3 v3.2.1/go.mod h1:LEpXwGt4pIqrE/XcTvCnZHT5MgZCV6Ub9q7yQzOFWr0= github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= @@ -29,12 +35,18 @@ github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxG github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU= github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/glebarez/go-sqlite v1.21.2 h1:3a6LFC4sKahUunAmynQKLZceZCOzUthkRkEAl9gAXWo= +github.com/glebarez/go-sqlite v1.21.2/go.mod h1:sfxdZyhQjTM2Wry3gVYWaW072Ri1WMdWJi0k6+3382k= +github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw= +github.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ= github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY= github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4= github.com/go-asn1-ber/asn1-ber v1.5.5 h1:MNHlNMBDgEKD4TcKr36vQN68BA00aDfjIt3/bD50WnA= @@ -47,20 +59,27 @@ github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMj github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= github.com/go-git/go-git/v5 v5.11.0 h1:XIZc1p+8YzypNr34itUfSvYJcv+eYdTnTvOZ2vD3cA4= github.com/go-git/go-git/v5 v5.11.0/go.mod h1:6GFcX2P3NM7FPBfpePbpLd21XxsgdAt+lKqXmCUiUCY= +github.com/go-gormigrate/gormigrate/v2 v2.1.2 h1:F/d1hpHbRAvKezziV2CC5KUE82cVe9zTgHSBoOOZ4CY= +github.com/go-gormigrate/gormigrate/v2 v2.1.2/go.mod h1:9nHVX6z3FCMCQPA7PThGcA55t22yKQfK/Dnsf5i7hUo= github.com/go-gorp/gorp/v3 v3.1.0 h1:ItKF/Vbuj31dmV4jxA1qblpSwkl9g1typ24xoe70IGs= github.com/go-gorp/gorp/v3 v3.1.0/go.mod h1:dLEjIyyRNiXvNZ8PSmzpt1GsWAUK8kjVhEpjH8TixEw= github.com/go-jose/go-jose/v3 v3.0.3 h1:fFKWeig/irsp7XD2zBxvnmA/XaRWp5V3CBsZXJF7G7k= github.com/go-jose/go-jose/v3 v3.0.3/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ= github.com/go-ldap/ldap/v3 v3.4.6 h1:ert95MdbiG7aWo/oPYp9btL3KJlMPKnP58r09rI8T+A= github.com/go-ldap/ldap/v3 v3.4.6/go.mod h1:IGMQANNtxpsOzj7uUAMjpGBaOVTC4DYyIy8VsTdxmtc= +github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= @@ -72,6 +91,8 @@ github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +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.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -87,8 +108,18 @@ github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/ github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.4.3 h1:cxFyXhxlvAifxnkKKdlxv8XqUf59tDlYjnV5YYfsJJY= +github.com/jackc/pgx/v5 v5.4.3/go.mod h1:Ig06C2Vu0t5qXC60W8sqIthScaEnFvojjj9dSljmHRA= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= +github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -104,8 +135,10 @@ github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhR github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI= -github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= +github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= +github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y= +github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= @@ -116,6 +149,9 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/poy/onpar v1.1.2 h1:QaNrNiZx0+Nar5dLgTVp5mXkyoVFIbepjyEoGSnhbAY= github.com/poy/onpar v1.1.2/go.mod h1:6X8FLNoxyr9kkmnlqpK6LSoiOtrO6MICtWwEuWkLjzg= +github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= @@ -137,6 +173,7 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -145,9 +182,12 @@ github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcU github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/thedevsaddam/gojsonq/v2 v2.5.2 h1:CoMVaYyKFsVj6TjU6APqAhAvC07hTI6IQen8PHzHYY0= github.com/thedevsaddam/gojsonq/v2 v2.5.2/go.mod h1:bv6Xa7kWy82uT0LnXPE2SzGqTj33TAEeR560MdJkiXs= +github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= +github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= go.etcd.io/bbolt v1.3.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI= go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -163,7 +203,9 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91 golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8= golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191105084925-a882066a44e0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= @@ -182,6 +224,7 @@ golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -189,6 +232,7 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -209,6 +253,7 @@ golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= @@ -228,6 +273,7 @@ golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ= golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= @@ -245,3 +291,18 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gorm.io/driver/mysql v1.5.6 h1:Ld4mkIickM+EliaQZQx3uOJDJHtrd70MxAUqWqlx3Y8= +gorm.io/driver/mysql v1.5.6/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM= +gorm.io/driver/postgres v1.5.7 h1:8ptbNJTDbEmhdr62uReG5BGkdQyeasu/FZHxI0IMGnM= +gorm.io/driver/postgres v1.5.7/go.mod h1:3e019WlBaYI5o5LIdNV+LyxCMNtLOQETBXL2h4chKpA= +gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= +gorm.io/gorm v1.25.10 h1:dQpO+33KalOA+aFYGlK+EfxcI5MbO7EP2yYygwh9h+s= +gorm.io/gorm v1.25.10/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= +modernc.org/libc v1.22.5 h1:91BNch/e5B0uPbJFgqbxXuOnxBQjlS//icfQEGmvyjE= +modernc.org/libc v1.22.5/go.mod h1:jj+Z7dTNX8fBScMVNRAYZ/jF91K8fdT2hYMThc3YjBY= +modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ= +modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/memory v1.5.0 h1:N+/8c5rE6EqugZwHii4IFsaJ7MUhoWX07J5tC/iI5Ds= +modernc.org/memory v1.5.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= +modernc.org/sqlite v1.23.1 h1:nrSBg4aRQQwq59JpvGEQ15tNxoO5pX/kUjcRNwSAGQM= +modernc.org/sqlite v1.23.1/go.mod h1:OrDj17Mggn6MhE+iPbBNf7RGKODDE9NFT0f3EwDzJqk= diff --git a/pkg/store/boltdb/access_keys.go b/pkg/store/boltdb/access_keys.go new file mode 100644 index 000000000..9fa53bfb1 --- /dev/null +++ b/pkg/store/boltdb/access_keys.go @@ -0,0 +1,252 @@ +package boltdb + +import ( + "context" + "errors" + + model "github.com/ansible-semaphore/semaphore/db" + "github.com/ansible-semaphore/semaphore/pkg/store" + "github.com/asdine/storm/v3" + "github.com/asdine/storm/v3/q" +) + +func (db *BoltdbStore) AccessKeyRefs(ctx context.Context, params model.AccessKeyParams) (*model.ObjectReferrers, error) { + if _, err := db.ShowAccessKey(ctx, params); err != nil { + return nil, err + } + + result := &model.ObjectReferrers{ + Repositories: make([]model.ObjectReferrer, 0), + Inventories: make([]model.ObjectReferrer, 0), + Templates: make([]model.ObjectReferrer, 0), + } + + { + rows := make([]*model.Repository, 0) + + if err := db.handle.Select( + q.Eq("project_id", params.ProjectID), + q.Eq("ssh_key_id", params.AccessKeyID), + ).Find( + &rows, + ); err != nil && !errors.Is(err, storm.ErrNotFound) { + return nil, err + } + + for _, row := range rows { + result.Repositories = append(result.Repositories, model.ObjectReferrer{ + ID: row.ID, + Name: row.Name, + }) + } + } + + { + rows := make([]*model.Inventory, 0) + + if err := db.handle.Select( + q.Eq("project_id", params.ProjectID), + q.Eq("ssh_key_id", params.AccessKeyID), + ).Find( + &rows, + ); err != nil && !errors.Is(err, storm.ErrNotFound) { + return nil, err + } + + for _, row := range rows { + result.Inventories = append(result.Inventories, model.ObjectReferrer{ + ID: row.ID, + Name: row.Name, + }) + } + } + + { + rows := make([]*model.Inventory, 0) + + if err := db.handle.Select( + q.Eq("project_id", params.ProjectID), + q.Eq("become_key_id", params.AccessKeyID), + ).Find( + &rows, + ); err != nil && !errors.Is(err, storm.ErrNotFound) { + return nil, err + } + + for _, row := range rows { + result.Inventories = append(result.Inventories, model.ObjectReferrer{ + ID: row.ID, + Name: row.Name, + }) + } + } + + { + rows := make([]*model.Inventory, 0) + + if err := db.handle.Select( + q.Eq("project_id", params.ProjectID), + q.Eq("vault_key_id", params.AccessKeyID), + ).Find( + &rows, + ); err != nil && !errors.Is(err, storm.ErrNotFound) { + return nil, err + } + + for _, row := range rows { + result.Templates = append(result.Templates, model.ObjectReferrer{ + ID: row.ID, + Name: row.Name, + }) + } + } + + return result, nil +} + +func (db *BoltdbStore) ListAccessKeys(ctx context.Context, params model.AccessKeyParams) ([]*model.AccessKey, error) { + records := make([]*model.AccessKey, 0) + + if err := db.sortQuery( + db.handle.Select( + q.Eq("project_id", params.ProjectID), + ), + model.AccessKeyProps, + params.Query, + ).Find( + &records, + ); err != nil && !errors.Is(err, storm.ErrNotFound) { + return nil, err + } + + return records, nil +} + +func (db *BoltdbStore) ShowAccessKey(ctx context.Context, params model.AccessKeyParams) (*model.AccessKey, error) { + record := &model.AccessKey{} + + if err := db.handle.Select( + q.And( + q.Eq("id", params.AccessKeyID), + q.Eq("project_id", params.ProjectID), + ), + ).First(record); err != nil { + if err == storm.ErrNotFound { + return nil, store.ErrRecordNotFound + } + + return nil, err + } + + return record, nil +} + +func (db *BoltdbStore) DeleteAccessKey(ctx context.Context, params model.AccessKeyParams) error { + tx, err := db.handle.Begin(true) + + if err != nil { + return err + } + + defer tx.Rollback() + + record := &model.AccessKey{ + ID: params.AccessKeyID, + ProjectID: ¶ms.ProjectID, + } + + if err := tx.DeleteStruct(record); err != nil { + return err + } + + return tx.Commit() +} + +func (db *BoltdbStore) RekeyAccessKey(ctx context.Context, params model.AccessKeyParams) error { + for i := 0; ; i++ { + records := make([]*model.AccessKey, 0) + + if err := db.sortQuery( + db.handle.Select(), + model.AccessKeyProps, + model.RetrieveQueryParams{Count: 100, Offset: i * 100}, + ).Find( + &records, + ); err != nil && !errors.Is(err, storm.ErrNotFound) { + return err + } + + if len(records) == 0 { + break + } + + for _, record := range records { + if err := record.DeserializeSecret2( + params.OldKey, + ); err != nil { + return err + } + + record.OverrideSecret = true + + if err := db.UpdateAccessKey( + ctx, + record, + ); err != nil && !errors.Is(err, store.ErrRecordNotFound) { + return err + } + } + } + + return nil +} + +func (db *BoltdbStore) CreateAccessKey(ctx context.Context, record *model.AccessKey) error { + tx, err := db.handle.Begin(true) + + if err != nil { + return err + } + + defer tx.Rollback() + + if err := record.Validate(true); err != nil { + return err + } + + if err := record.SerializeSecret(); err != nil { + return err + } + + if err := tx.Save(record); err != nil { + return err + } + + return tx.Commit() +} + +func (db *BoltdbStore) UpdateAccessKey(ctx context.Context, record *model.AccessKey) error { + tx, err := db.handle.Begin(true) + + if err != nil { + return err + } + + defer tx.Rollback() + + if err := record.Validate( + record.OverrideSecret, + ); err != nil { + return err + } + + if err := record.SerializeSecret(); err != nil { + return err + } + + if err := tx.Save(record); err != nil { + return err + } + + return tx.Commit() +} diff --git a/pkg/store/boltdb/boltdb.go b/pkg/store/boltdb/boltdb.go new file mode 100644 index 000000000..9a0fa49a3 --- /dev/null +++ b/pkg/store/boltdb/boltdb.go @@ -0,0 +1,184 @@ +package boltdb + +import ( + "context" + "os" + "slices" + "strconv" + "time" + + "github.com/ansible-semaphore/semaphore/db" + model "github.com/ansible-semaphore/semaphore/db" + "github.com/ansible-semaphore/semaphore/pkg/store" + "github.com/ansible-semaphore/semaphore/util" + "github.com/asdine/storm/v3" + "github.com/asdine/storm/v3/q" + bolt "go.etcd.io/bbolt" +) + +// BoltdbStore implements the Store interface. +type BoltdbStore struct { + database string + perms os.FileMode + timeout time.Duration + + handle *storm.DB +} + +// Info returns some basic db informations. +func (db *BoltdbStore) Info() map[string]interface{} { + result := make(map[string]interface{}) + result["driver"] = "boltdb" + result["database"] = db.database + result["perms"] = db.perms.String() + result["timeout"] = db.timeout.String() + + return result +} + +// Prepare is preparing some database behavior. +func (db *BoltdbStore) Prepare() error { + return nil +} + +// Open simply opens the database connection. +func (db *BoltdbStore) Open() error { + handle, err := storm.Open( + db.database, + storm.BoltOptions( + db.perms, + &bolt.Options{ + Timeout: db.timeout, + }, + ), + ) + + if err != nil { + return err + } + + db.handle = handle + return db.Prepare() +} + +// Close simply closes the database connection. +func (db *BoltdbStore) Close() error { + return db.handle.Close() +} + +// Ping just tests the database connection. +func (db *BoltdbStore) Ping() error { + return nil +} + +// Migrate executes required db migrations. +func (db *BoltdbStore) Migrate() error { + return nil +} + +// Admin creates an initial admin user within the database. +func (db *BoltdbStore) Admin(username, password, email string) error { + admin := &model.User{} + + if err := db.handle.Select( + q.Eq("username", username), + ).First(admin); err != nil && err != storm.ErrNotFound { + return err + } + + admin.Username = username + admin.Password = password + admin.Email = email + admin.Admin = true + + if admin.Name == "" { + admin.Name = "Admin" + } + + if admin.ID == 0 { + if err := db.CreateUser( + context.Background(), + admin, + ); err != nil { + return err + } + } else { + if err := db.UpdateUser( + context.Background(), + admin, + ); err != nil { + return err + } + } + + return nil +} + +func (db *BoltdbStore) sortQuery(query storm.Query, props db.ObjectProps, params model.RetrieveQueryParams) storm.Query { + if params.SortInverted { + query = query.Reverse() + } + + orderColumn := props.DefaultSortingColumn + if slices.Contains(props.SortableColumns, params.SortBy) { + orderColumn = params.SortBy + } + + if orderColumn != "" { + query = query.OrderBy(orderColumn) + } + + if params.Count > 0 { + query = query.Limit(params.Count) + } + + if params.Offset > 0 { + query = query.Skip(params.Offset) + } + + return query +} + +// NewStore initializes a new gorm store instance +func NewStore(cfg util.DbConfig) (store.Store, error) { + client := &BoltdbStore{ + database: cfg.Hostname, + } + + if val, ok := cfg.Options["perms"]; ok { + res, err := strconv.ParseUint(val, 8, 32) + + if err != nil { + client.perms = os.FileMode(0600) + } else { + client.perms = os.FileMode(res) + } + } else { + client.perms = os.FileMode(0600) + } + + if val, ok := cfg.Options["timeout"]; ok { + res, err := time.ParseDuration(val) + + if err != nil { + client.timeout = 1 * time.Second + } else { + client.timeout = res + } + } else { + client.timeout = 1 * time.Second + } + + return client, nil +} + +// MustStore simply calls NewStore and panics on an error. +func MustStore(cfg util.DbConfig) store.Store { + s, err := NewStore(cfg) + + if err != nil { + panic(err) + } + + return s +} diff --git a/pkg/store/boltdb/envs.go b/pkg/store/boltdb/envs.go new file mode 100644 index 000000000..fab9f746d --- /dev/null +++ b/pkg/store/boltdb/envs.go @@ -0,0 +1,31 @@ +package boltdb + +import ( + "context" + + model "github.com/ansible-semaphore/semaphore/db" +) + +func (db *BoltdbStore) EnvRefs(ctx context.Context, params model.EnvParams) (*model.ObjectReferrers, error) { + return nil, nil +} + +func (db *BoltdbStore) ListEnvs(ctx context.Context, params model.EnvParams) ([]*model.Environment, error) { + return nil, nil +} + +func (db *BoltdbStore) ShowEnv(ctx context.Context, params model.EnvParams) (*model.Environment, error) { + return nil, nil +} + +func (db *BoltdbStore) DeleteEnv(ctx context.Context, params model.EnvParams) error { + return nil +} + +func (db *BoltdbStore) CreateEnv(ctx context.Context, record *model.Environment) error { + return nil +} + +func (db *BoltdbStore) UpdateEnv(ctx context.Context, record *model.Environment) error { + return nil +} diff --git a/pkg/store/boltdb/events.go b/pkg/store/boltdb/events.go new file mode 100644 index 000000000..fbb816a1a --- /dev/null +++ b/pkg/store/boltdb/events.go @@ -0,0 +1,19 @@ +package boltdb + +import ( + "context" + + model "github.com/ansible-semaphore/semaphore/db" +) + +func (db *BoltdbStore) UserEvents(ctx context.Context, params model.EventParams) ([]*model.Event, error) { + return nil, nil +} + +func (db *BoltdbStore) ProjectEvents(ctx context.Context, params model.EventParams) ([]*model.Event, error) { + return nil, nil +} + +func (db *BoltdbStore) CreateEvent(ctx context.Context, record *model.Event) error { + return nil +} diff --git a/pkg/store/boltdb/integration_aliases.go b/pkg/store/boltdb/integration_aliases.go new file mode 100644 index 000000000..70419948d --- /dev/null +++ b/pkg/store/boltdb/integration_aliases.go @@ -0,0 +1,19 @@ +package boltdb + +import ( + "context" + + model "github.com/ansible-semaphore/semaphore/db" +) + +func (db *BoltdbStore) ListIntegrationAliases(ctx context.Context, params model.IntegrationAliasParams) ([]*model.IntegrationAlias, error) { + return nil, nil +} + +func (db *BoltdbStore) DeleteIntegrationAlias(ctx context.Context, params model.IntegrationAliasParams) error { + return nil +} + +func (db *BoltdbStore) CreateIntegrationAlias(ctx context.Context, record *model.IntegrationAlias) error { + return nil +} diff --git a/pkg/store/boltdb/integration_extract_values.go b/pkg/store/boltdb/integration_extract_values.go new file mode 100644 index 000000000..ef53b5809 --- /dev/null +++ b/pkg/store/boltdb/integration_extract_values.go @@ -0,0 +1,31 @@ +package boltdb + +import ( + "context" + + model "github.com/ansible-semaphore/semaphore/db" +) + +func (db *BoltdbStore) IntegrationExtractValueRefs(ctx context.Context, params model.IntegrationExtractValueParams) (*model.IntegrationExtractorChildReferrers, error) { + return nil, nil +} + +func (db *BoltdbStore) ListIntegrationExtractValues(ctx context.Context, params model.IntegrationExtractValueParams) ([]*model.IntegrationExtractValue, error) { + return nil, nil +} + +func (db *BoltdbStore) ShowIntegrationExtractValue(ctx context.Context, params model.IntegrationExtractValueParams) (*model.IntegrationExtractValue, error) { + return nil, nil +} + +func (db *BoltdbStore) DeleteIntegrationExtractValue(ctx context.Context, params model.IntegrationExtractValueParams) error { + return nil +} + +func (db *BoltdbStore) CreateIntegrationExtractValue(ctx context.Context, params model.IntegrationExtractValueParams, record *model.IntegrationExtractValue) error { + return nil +} + +func (db *BoltdbStore) UpdateIntegrationExtractValue(ctx context.Context, params model.IntegrationExtractValueParams, record *model.IntegrationExtractValue) error { + return nil +} diff --git a/pkg/store/boltdb/integration_matchers.go b/pkg/store/boltdb/integration_matchers.go new file mode 100644 index 000000000..31ed01be7 --- /dev/null +++ b/pkg/store/boltdb/integration_matchers.go @@ -0,0 +1,31 @@ +package boltdb + +import ( + "context" + + model "github.com/ansible-semaphore/semaphore/db" +) + +func (db *BoltdbStore) IntegrationMatcherRefs(ctx context.Context, params model.IntegrationMatcherParams) (*model.IntegrationExtractorChildReferrers, error) { + return nil, nil +} + +func (db *BoltdbStore) ListIntegrationMatchers(ctx context.Context, params model.IntegrationMatcherParams) ([]*model.IntegrationMatcher, error) { + return nil, nil +} + +func (db *BoltdbStore) ShowIntegrationMatcher(ctx context.Context, params model.IntegrationMatcherParams) (*model.IntegrationMatcher, error) { + return nil, nil +} + +func (db *BoltdbStore) DeleteIntegrationMatcher(ctx context.Context, params model.IntegrationMatcherParams) error { + return nil +} + +func (db *BoltdbStore) CreateIntegrationMatcher(ctx context.Context, params model.IntegrationMatcherParams, record *model.IntegrationMatcher) error { + return nil +} + +func (db *BoltdbStore) UpdateIntegrationMatcher(ctx context.Context, params model.IntegrationMatcherParams, record *model.IntegrationMatcher) error { + return nil +} diff --git a/pkg/store/boltdb/integrations.go b/pkg/store/boltdb/integrations.go new file mode 100644 index 000000000..b17adb6ac --- /dev/null +++ b/pkg/store/boltdb/integrations.go @@ -0,0 +1,39 @@ +package boltdb + +import ( + "context" + + model "github.com/ansible-semaphore/semaphore/db" +) + +func (db *BoltdbStore) IntegrationRefs(ctx context.Context, params model.IntegrationParams) (*model.IntegrationReferrers, error) { + return nil, nil +} + +func (db *BoltdbStore) SearchableIntegrations(ctx context.Context, params model.IntegrationParams) ([]*model.Integration, error) { + return nil, nil +} + +func (db *BoltdbStore) AliasedIntegrations(ctx context.Context, params model.IntegrationParams) ([]*model.Integration, error) { + return nil, nil +} + +func (db *BoltdbStore) ListIntegrations(ctx context.Context, params model.IntegrationParams) ([]*model.Integration, error) { + return nil, nil +} + +func (db *BoltdbStore) ShowIntegration(ctx context.Context, params model.IntegrationParams) (*model.Integration, error) { + return nil, nil +} + +func (db *BoltdbStore) DeleteIntegration(ctx context.Context, params model.IntegrationParams) error { + return nil +} + +func (db *BoltdbStore) CreateIntegration(ctx context.Context, record *model.Integration) error { + return nil +} + +func (db *BoltdbStore) UpdateIntegration(ctx context.Context, record *model.Integration) error { + return nil +} diff --git a/pkg/store/boltdb/inventories.go b/pkg/store/boltdb/inventories.go new file mode 100644 index 000000000..8d5303174 --- /dev/null +++ b/pkg/store/boltdb/inventories.go @@ -0,0 +1,31 @@ +package boltdb + +import ( + "context" + + model "github.com/ansible-semaphore/semaphore/db" +) + +func (db *BoltdbStore) InventoryRefs(ctx context.Context, params model.InventoryParams) (*model.ObjectReferrers, error) { + return nil, nil +} + +func (db *BoltdbStore) ListInventories(ctx context.Context, params model.InventoryParams) ([]*model.Inventory, error) { + return nil, nil +} + +func (db *BoltdbStore) ShowInventory(ctx context.Context, params model.InventoryParams) (*model.Inventory, error) { + return nil, nil +} + +func (db *BoltdbStore) DeleteInventory(ctx context.Context, params model.InventoryParams) error { + return nil +} + +func (db *BoltdbStore) CreateInventory(ctx context.Context, record *model.Inventory) error { + return nil +} + +func (db *BoltdbStore) UpdateInventory(ctx context.Context, record *model.Inventory) error { + return nil +} diff --git a/pkg/store/boltdb/members.go b/pkg/store/boltdb/members.go new file mode 100644 index 000000000..1b3ec965a --- /dev/null +++ b/pkg/store/boltdb/members.go @@ -0,0 +1,27 @@ +package boltdb + +import ( + "context" + + model "github.com/ansible-semaphore/semaphore/db" +) + +func (db *BoltdbStore) ListMembers(ctx context.Context, params model.MemberParams) ([]*model.UserWithProjectRole, error) { + return nil, nil +} + +func (db *BoltdbStore) ShowMember(ctx context.Context, params model.MemberParams) (*model.ProjectUser, error) { + return nil, nil +} + +func (db *BoltdbStore) DeleteMember(ctx context.Context, params model.MemberParams) error { + return nil +} + +func (db *BoltdbStore) CreateMember(ctx context.Context, record *model.ProjectUser) error { + return nil +} + +func (db *BoltdbStore) UpdateMember(ctx context.Context, record *model.ProjectUser) error { + return nil +} diff --git a/pkg/store/boltdb/migrate.go b/pkg/store/boltdb/migrate.go new file mode 100644 index 000000000..473fb8314 --- /dev/null +++ b/pkg/store/boltdb/migrate.go @@ -0,0 +1 @@ +package boltdb diff --git a/pkg/store/boltdb/options.go b/pkg/store/boltdb/options.go new file mode 100644 index 000000000..3b720512c --- /dev/null +++ b/pkg/store/boltdb/options.go @@ -0,0 +1,13 @@ +package boltdb + +import ( + "context" +) + +func (db *BoltdbStore) GetOption(ctx context.Context, key string) error { + return nil +} + +func (db *BoltdbStore) SetOption(ctx context.Context, key, val string) error { + return nil +} diff --git a/pkg/store/boltdb/projects.go b/pkg/store/boltdb/projects.go new file mode 100644 index 000000000..3b6cc39f1 --- /dev/null +++ b/pkg/store/boltdb/projects.go @@ -0,0 +1,27 @@ +package boltdb + +import ( + "context" + + model "github.com/ansible-semaphore/semaphore/db" +) + +func (db *BoltdbStore) ListProjects(ctx context.Context, params model.ProjectParams) ([]*model.Project, error) { + return nil, nil +} + +func (db *BoltdbStore) ShowProject(ctx context.Context, params model.ProjectParams) (*model.Project, error) { + return nil, nil +} + +func (db *BoltdbStore) DeleteProject(ctx context.Context, params model.ProjectParams) error { + return nil +} + +func (db *BoltdbStore) CreateProject(ctx context.Context, record *model.Project) error { + return nil +} + +func (db *BoltdbStore) UpdateProject(ctx context.Context, record *model.Project) error { + return nil +} diff --git a/pkg/store/boltdb/repos.go b/pkg/store/boltdb/repos.go new file mode 100644 index 000000000..eb1155c48 --- /dev/null +++ b/pkg/store/boltdb/repos.go @@ -0,0 +1,31 @@ +package boltdb + +import ( + "context" + + model "github.com/ansible-semaphore/semaphore/db" +) + +func (db *BoltdbStore) RepoRefs(ctx context.Context, params model.RepoParams) (*model.ObjectReferrers, error) { + return nil, nil +} + +func (db *BoltdbStore) ListRepos(ctx context.Context, params model.RepoParams) ([]*model.Repository, error) { + return nil, nil +} + +func (db *BoltdbStore) ShowRepo(ctx context.Context, params model.RepoParams) (*model.Repository, error) { + return nil, nil +} + +func (db *BoltdbStore) DeleteRepo(ctx context.Context, params model.RepoParams) error { + return nil +} + +func (db *BoltdbStore) CreateRepo(ctx context.Context, record *model.Repository) error { + return nil +} + +func (db *BoltdbStore) UpdateRepo(ctx context.Context, record *model.Repository) error { + return nil +} diff --git a/pkg/store/boltdb/runners.go b/pkg/store/boltdb/runners.go new file mode 100644 index 000000000..e20ab5d91 --- /dev/null +++ b/pkg/store/boltdb/runners.go @@ -0,0 +1,193 @@ +package boltdb + +import ( + "context" + "errors" + + model "github.com/ansible-semaphore/semaphore/db" + "github.com/ansible-semaphore/semaphore/pkg/store" + "github.com/asdine/storm/v3" + "github.com/asdine/storm/v3/q" +) + +func (db *BoltdbStore) ListGlobalRunners(_ context.Context, params model.RunnerParams) ([]*model.Runner, error) { + records := make([]*model.Runner, 0) + + if err := db.handle.All( + &records, + ); err != nil && !errors.Is(err, storm.ErrNotFound) { + return nil, err + } + + return records, nil +} + +func (db *BoltdbStore) ShowGlobalRunner(_ context.Context, params model.RunnerParams) (*model.Runner, error) { + record := &model.Runner{} + + if err := db.handle.Select( + q.Eq("id", params.RunnerID), + ).First(record); err != nil { + if err == storm.ErrNotFound { + return nil, store.ErrRecordNotFound + } + + return nil, err + } + + return record, nil +} + +func (db *BoltdbStore) DeleteGlobalRunner(_ context.Context, params model.RunnerParams) error { + tx, err := db.handle.Begin(true) + + if err != nil { + return err + } + + defer tx.Rollback() + + record := &model.Runner{ + ID: params.RunnerID, + } + + if err := tx.DeleteStruct(record); err != nil { + return err + } + + return tx.Commit() +} + +func (db *BoltdbStore) CreateGlobalRunner(_ context.Context, record *model.Runner) error { + tx, err := db.handle.Begin(true) + + if err != nil { + return err + } + + defer tx.Rollback() + + if err := tx.Save(record); err != nil { + return err + } + + if err := tx.Commit(); err != nil { + return err + } + + return nil +} + +func (db *BoltdbStore) UpdateGlobalRunner(_ context.Context, record *model.Runner) error { + tx, err := db.handle.Begin(true) + + if err != nil { + return err + } + + defer tx.Rollback() + + if err := tx.Save(record); err != nil { + return err + } + + if err := tx.Commit(); err != nil { + return err + } + + return nil +} + +func (db *BoltdbStore) ListProjectRunner(_ context.Context, params model.RunnerParams) ([]*model.Runner, error) { + records := make([]*model.Runner, 0) + + if err := db.handle.Select( + q.Eq("project_id", params.ProjectID), + ).Find( + &records, + ); err != nil && !errors.Is(err, storm.ErrNotFound) { + return nil, err + } + + return records, nil +} + +func (db *BoltdbStore) ShowProjectRunner(_ context.Context, params model.RunnerParams) (*model.Runner, error) { + record := &model.Runner{} + + if err := db.handle.Select( + q.And( + q.Eq("id", params.RunnerID), + q.Eq("project_id", params.ProjectID), + ), + ).First(record); err != nil { + if err == storm.ErrNotFound { + return nil, store.ErrRecordNotFound + } + + return nil, err + } + + return record, nil +} + +func (db *BoltdbStore) DeleteProjectRunner(_ context.Context, params model.RunnerParams) error { + tx, err := db.handle.Begin(true) + + if err != nil { + return err + } + + defer tx.Rollback() + + record := &model.Runner{ + ID: params.RunnerID, + ProjectID: ¶ms.ProjectID, + } + + if err := tx.DeleteStruct(record); err != nil { + return err + } + + return tx.Commit() +} + +func (db *BoltdbStore) CreateProjectRunner(_ context.Context, record *model.Runner) error { + tx, err := db.handle.Begin(true) + + if err != nil { + return err + } + + defer tx.Rollback() + + if err := tx.Save(record); err != nil { + return err + } + + if err := tx.Commit(); err != nil { + return err + } + + return nil +} + +func (db *BoltdbStore) UpdateProjectRunner(_ context.Context, record *model.Runner) error { + tx, err := db.handle.Begin(true) + + if err != nil { + return err + } + + defer tx.Rollback() + + if err := tx.Save(record); err != nil { + return err + } + + if err := tx.Commit(); err != nil { + return err + } + + return nil +} diff --git a/pkg/store/boltdb/schedules.go b/pkg/store/boltdb/schedules.go new file mode 100644 index 000000000..e82f22c7e --- /dev/null +++ b/pkg/store/boltdb/schedules.go @@ -0,0 +1,35 @@ +package boltdb + +import ( + "context" + + model "github.com/ansible-semaphore/semaphore/db" +) + +func (db *BoltdbStore) TemplateSchedules(ctx context.Context, params model.ScheduleParams) ([]*model.Schedule, error) { + return nil, nil +} + +func (db *BoltdbStore) ListSchedules(ctx context.Context, params model.ScheduleParams) ([]*model.Schedule, error) { + return nil, nil +} + +func (db *BoltdbStore) ShowSchedule(ctx context.Context, params model.ScheduleParams) (*model.Schedule, error) { + return nil, nil +} + +func (db *BoltdbStore) DeleteSchedule(ctx context.Context, params model.ScheduleParams) error { + return nil +} + +func (db *BoltdbStore) HashSchedule(ctx context.Context, params model.ScheduleParams) error { + return nil +} + +func (db *BoltdbStore) CreateSchedule(ctx context.Context, record *model.Schedule) error { + return nil +} + +func (db *BoltdbStore) UpdateSchedule(ctx context.Context, record *model.Schedule) error { + return nil +} diff --git a/pkg/store/boltdb/sessions.go b/pkg/store/boltdb/sessions.go new file mode 100644 index 000000000..2145371f0 --- /dev/null +++ b/pkg/store/boltdb/sessions.go @@ -0,0 +1,23 @@ +package boltdb + +import ( + "context" + + model "github.com/ansible-semaphore/semaphore/db" +) + +func (db *BoltdbStore) ShowSession(ctx context.Context, params model.SessionParams) (*model.Session, error) { + return nil, nil +} + +func (db *BoltdbStore) ExpireSession(ctx context.Context, params model.SessionParams) error { + return nil +} + +func (db *BoltdbStore) TouchSession(ctx context.Context, params model.SessionParams) error { + return nil +} + +func (db *BoltdbStore) CreateSession(ctx context.Context, record *model.Session) error { + return nil +} diff --git a/pkg/store/boltdb/tasks.go b/pkg/store/boltdb/tasks.go new file mode 100644 index 000000000..bd770bf24 --- /dev/null +++ b/pkg/store/boltdb/tasks.go @@ -0,0 +1,39 @@ +package boltdb + +import ( + "context" + + model "github.com/ansible-semaphore/semaphore/db" +) + +func (db *BoltdbStore) TemplateTasks(ctx context.Context, params model.TaskParams) ([]*model.TaskWithTpl, error) { + return nil, nil +} + +func (db *BoltdbStore) ProjectTasks(ctx context.Context, params model.TaskParams) ([]*model.TaskWithTpl, error) { + return nil, nil +} + +func (db *BoltdbStore) ShowTask(ctx context.Context, params model.TaskParams) (*model.Task, error) { + return nil, nil +} + +func (db *BoltdbStore) DeleteTask(ctx context.Context, params model.TaskParams) error { + return nil +} + +func (db *BoltdbStore) CreateTask(ctx context.Context, record *model.Task) error { + return nil +} + +func (db *BoltdbStore) UpdateTask(ctx context.Context, record *model.Task) error { + return nil +} + +func (db *BoltdbStore) PushOutput(ctx context.Context, record *model.TaskOutput) error { + return nil +} + +func (db *BoltdbStore) GetOutputs(ctx context.Context, params model.TaskParams) ([]*model.TaskOutput, error) { + return nil, nil +} diff --git a/pkg/store/boltdb/templates.go b/pkg/store/boltdb/templates.go new file mode 100644 index 000000000..ed444216b --- /dev/null +++ b/pkg/store/boltdb/templates.go @@ -0,0 +1,31 @@ +package boltdb + +import ( + "context" + + model "github.com/ansible-semaphore/semaphore/db" +) + +func (db *BoltdbStore) TemplateRefs(ctx context.Context, params model.TemplateParams) (*model.ObjectReferrers, error) { + return nil, nil +} + +func (db *BoltdbStore) ListTemplates(ctx context.Context, params model.TemplateParams) ([]*model.Template, error) { + return nil, nil +} + +func (db *BoltdbStore) ShowTemplate(ctx context.Context, params model.TemplateParams) (*model.Template, error) { + return nil, nil +} + +func (db *BoltdbStore) DeleteTemplate(ctx context.Context, params model.TemplateParams) error { + return nil +} + +func (db *BoltdbStore) CreateTemplate(ctx context.Context, record *model.Template) error { + return nil +} + +func (db *BoltdbStore) UpdateTemplate(ctx context.Context, record *model.Template) error { + return nil +} diff --git a/pkg/store/boltdb/tokens.go b/pkg/store/boltdb/tokens.go new file mode 100644 index 000000000..f763a3b92 --- /dev/null +++ b/pkg/store/boltdb/tokens.go @@ -0,0 +1,27 @@ +package boltdb + +import ( + "context" + + model "github.com/ansible-semaphore/semaphore/db" +) + +func (db *BoltdbStore) ListTokens(ctx context.Context, params model.TokenParams) ([]*model.APIToken, error) { + return nil, nil +} + +func (db *BoltdbStore) ShowToken(ctx context.Context, params model.TokenParams) (*model.APIToken, error) { + return nil, nil +} + +func (db *BoltdbStore) DeleteToken(ctx context.Context, params model.TokenParams) error { + return nil +} + +func (db *BoltdbStore) ExpireToken(ctx context.Context, params model.TokenParams) error { + return nil +} + +func (db *BoltdbStore) CreateToken(ctx context.Context, record *model.APIToken) error { + return nil +} diff --git a/pkg/store/boltdb/users.go b/pkg/store/boltdb/users.go new file mode 100644 index 000000000..89b920952 --- /dev/null +++ b/pkg/store/boltdb/users.go @@ -0,0 +1,35 @@ +package boltdb + +import ( + "context" + + model "github.com/ansible-semaphore/semaphore/db" +) + +func (db *BoltdbStore) AdminUsers(ctxy context.Context, params model.UserParams) ([]*model.User, error) { + return nil, nil +} + +func (db *BoltdbStore) ListUsers(ctx context.Context, params model.UserParams) ([]*model.User, error) { + return nil, nil +} + +func (db *BoltdbStore) ShowUser(ctx context.Context, params model.UserParams) (*model.User, error) { + return nil, nil +} + +func (db *BoltdbStore) DeleteUser(ctx context.Context, params model.UserParams) error { + return nil +} + +func (db *BoltdbStore) CreateUser(ctx context.Context, record *model.User) error { + return nil +} + +func (db *BoltdbStore) UpdateUser(ctx context.Context, record *model.User) error { + return nil +} + +func (db *BoltdbStore) UpdatePassword(ctx context.Context, params model.UserParams) error { + return nil +} diff --git a/pkg/store/boltdb/views.go b/pkg/store/boltdb/views.go new file mode 100644 index 000000000..50b5e5f1b --- /dev/null +++ b/pkg/store/boltdb/views.go @@ -0,0 +1,31 @@ +package boltdb + +import ( + "context" + + model "github.com/ansible-semaphore/semaphore/db" +) + +func (db *BoltdbStore) ListViews(ctx context.Context, params model.ViewParams) ([]*model.View, error) { + return nil, nil +} + +func (db *BoltdbStore) ShowView(ctx context.Context, params model.ViewParams) (*model.View, error) { + return nil, nil +} + +func (db *BoltdbStore) DeleteView(ctx context.Context, params model.ViewParams) error { + return nil +} + +func (db *BoltdbStore) PositionView(ctx context.Context, params model.ViewParams) error { + return nil +} + +func (db *BoltdbStore) CreateView(ctx context.Context, record *model.View) error { + return nil +} + +func (db *BoltdbStore) UpdateView(ctx context.Context, record *model.View) error { + return nil +} diff --git a/pkg/store/gormdb/access_keys.go b/pkg/store/gormdb/access_keys.go new file mode 100644 index 000000000..88f2c0fe5 --- /dev/null +++ b/pkg/store/gormdb/access_keys.go @@ -0,0 +1,216 @@ +package gormdb + +import ( + "context" + + model "github.com/ansible-semaphore/semaphore/db" + "github.com/ansible-semaphore/semaphore/pkg/store" + "github.com/pkg/errors" + "gorm.io/gorm" +) + +func (db *GormdbStore) AccessKeyRefs(ctx context.Context, params model.AccessKeyParams) (*model.ObjectReferrers, error) { + record := &model.AccessKey{} + + if err := db.handle.WithContext(ctx).Where( + "id = ?", + params.AccessKeyID, + ).Where( + "project_id = ?", + params.ProjectID, + ).Preload( + "RepositorySSHKeys", + ).Preload( + "InventorySSHKeys", + ).Preload( + "InventoryBecomeKeys", + ).Preload( + "TemplateVaultKeys", + ).First( + record, + ).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, store.ErrRecordNotFound + } + + return nil, err + } + + result := &model.ObjectReferrers{ + Repositories: make([]model.ObjectReferrer, 0), + Inventories: make([]model.ObjectReferrer, 0), + Templates: make([]model.ObjectReferrer, 0), + } + + for _, row := range record.RepositorySSHKeys { + result.Repositories = append(result.Repositories, model.ObjectReferrer{ + ID: row.ID, + Name: row.Name, + }) + } + + for _, row := range record.InventorySSHKeys { + result.Inventories = append(result.Inventories, model.ObjectReferrer{ + ID: row.ID, + Name: row.Name, + }) + } + + for _, row := range record.InventoryBecomeKeys { + result.Inventories = append(result.Inventories, model.ObjectReferrer{ + ID: row.ID, + Name: row.Name, + }) + } + + for _, row := range record.TemplateVaultKeys { + result.Templates = append(result.Templates, model.ObjectReferrer{ + ID: row.ID, + Name: row.Name, + }) + } + + return result, nil +} + +func (db *GormdbStore) ListAccessKeys(ctx context.Context, params model.AccessKeyParams) ([]*model.AccessKey, error) { + records := make([]*model.AccessKey, 0) + + err := db.sortQuery( + db.handle.WithContext(ctx), + model.AccessKeyProps, + params.Query, + ).Where( + "project_id = ?", + params.ProjectID, + ).Find( + &records, + ).Error + + return records, err +} + +func (db *GormdbStore) ShowAccessKey(ctx context.Context, params model.AccessKeyParams) (*model.AccessKey, error) { + record := &model.AccessKey{} + + err := db.handle.WithContext(ctx).Where( + "id = ?", + params.AccessKeyID, + ).Where( + "project_id = ?", + params.ProjectID, + ).First( + record, + ).Error + + if err == gorm.ErrRecordNotFound { + return record, store.ErrRecordNotFound + } + + return record, err +} + +func (db *GormdbStore) DeleteAccessKey(ctx context.Context, params model.AccessKeyParams) error { + tx := db.handle.WithContext(ctx).Begin() + defer tx.Rollback() + + if err := tx.Where( + "id = ?", + params.AccessKeyID, + ).Where( + "project_id = ?", + params.ProjectID, + ).Delete( + &model.AccessKey{}, + ).Error; err != nil { + if err == gorm.ErrRecordNotFound { + return store.ErrRecordNotFound + } + + return err + } + + return tx.Commit().Error +} + +func (db *GormdbStore) RekeyAccessKey(ctx context.Context, params model.AccessKeyParams) error { + for i := 0; ; i++ { + records := make([]*model.AccessKey, 0) + + if err := db.sortQuery( + db.handle.WithContext(ctx), + model.AccessKeyProps, + model.RetrieveQueryParams{Count: 100, Offset: i * 100}, + ).Where( + "project_id = ?", + params.ProjectID, + ).Find( + &records, + ).Error; err != nil { + return err + } + + if len(records) == 0 { + break + } + + for _, record := range records { + if err := record.DeserializeSecret2( + params.OldKey, + ); err != nil { + return err + } + + record.OverrideSecret = true + + if err := db.UpdateAccessKey( + ctx, + record, + ); err != nil && !errors.Is(err, store.ErrRecordNotFound) { + return err + } + } + } + + return nil +} + +func (db *GormdbStore) CreateAccessKey(ctx context.Context, record *model.AccessKey) error { + tx := db.handle.WithContext(ctx).Begin() + defer tx.Rollback() + + if err := record.Validate(true); err != nil { + return err + } + + if err := record.SerializeSecret(); err != nil { + return err + } + + if err := tx.Create(record).Error; err != nil { + return err + } + + return tx.Commit().Error +} + +func (db *GormdbStore) UpdateAccessKey(ctx context.Context, record *model.AccessKey) error { + tx := db.handle.WithContext(ctx).Begin() + defer tx.Rollback() + + if err := record.Validate( + record.OverrideSecret, + ); err != nil { + return err + } + + if err := record.SerializeSecret(); err != nil { + return err + } + + if err := tx.Save(record).Error; err != nil { + return err + } + + return tx.Commit().Error +} diff --git a/pkg/store/gormdb/envs.go b/pkg/store/gormdb/envs.go new file mode 100644 index 000000000..a476c742d --- /dev/null +++ b/pkg/store/gormdb/envs.go @@ -0,0 +1,31 @@ +package gormdb + +import ( + "context" + + model "github.com/ansible-semaphore/semaphore/db" +) + +func (db *GormdbStore) EnvRefs(ctx context.Context, params model.EnvParams) (*model.ObjectReferrers, error) { + return nil, nil +} + +func (db *GormdbStore) ListEnvs(ctx context.Context, params model.EnvParams) ([]*model.Environment, error) { + return nil, nil +} + +func (db *GormdbStore) ShowEnv(ctx context.Context, params model.EnvParams) (*model.Environment, error) { + return nil, nil +} + +func (db *GormdbStore) DeleteEnv(ctx context.Context, params model.EnvParams) error { + return nil +} + +func (db *GormdbStore) CreateEnv(ctx context.Context, record *model.Environment) error { + return nil +} + +func (db *GormdbStore) UpdateEnv(ctx context.Context, record *model.Environment) error { + return nil +} diff --git a/pkg/store/gormdb/events.go b/pkg/store/gormdb/events.go new file mode 100644 index 000000000..92aaeecf5 --- /dev/null +++ b/pkg/store/gormdb/events.go @@ -0,0 +1,19 @@ +package gormdb + +import ( + "context" + + model "github.com/ansible-semaphore/semaphore/db" +) + +func (db *GormdbStore) UserEvents(ctx context.Context, params model.EventParams) ([]*model.Event, error) { + return nil, nil +} + +func (db *GormdbStore) ProjectEvents(ctx context.Context, params model.EventParams) ([]*model.Event, error) { + return nil, nil +} + +func (db *GormdbStore) CreateEvent(ctx context.Context, record *model.Event) error { + return nil +} diff --git a/pkg/store/gormdb/gormdb.go b/pkg/store/gormdb/gormdb.go new file mode 100644 index 000000000..c26baa871 --- /dev/null +++ b/pkg/store/gormdb/gormdb.go @@ -0,0 +1,375 @@ +package gormdb + +import ( + "context" + "fmt" + "net/url" + "slices" + "strconv" + "strings" + "time" + + "github.com/ansible-semaphore/semaphore/db" + model "github.com/ansible-semaphore/semaphore/db" + "github.com/ansible-semaphore/semaphore/pkg/store" + "github.com/ansible-semaphore/semaphore/util" + "github.com/glebarez/sqlite" + "github.com/go-gormigrate/gormigrate/v2" + "github.com/pkg/errors" + "gorm.io/driver/mysql" + "gorm.io/driver/postgres" + "gorm.io/gorm" +) + +// GormdbStore implements the Store interface. +type GormdbStore struct { + driver string + username string + password string + hostname string + database string + maxOpenConns int + maxIdleConns int + connMaxLifetime time.Duration + params url.Values + + handle *gorm.DB +} + +// Info returns some basic db informations. +func (s *GormdbStore) Info() map[string]interface{} { + result := make(map[string]interface{}) + result["driver"] = s.driver + result["database"] = s.database + + if s.hostname != "" { + result["hostname"] = s.hostname + } + + if s.username != "" { + result["username"] = s.username + } + + return result +} + +// Prepare is preparing some database behavior. +func (s *GormdbStore) Prepare() error { + sqldb, err := s.handle.DB() + + if err != nil { + return err + } + + switch s.driver { + case "mysql", "mariadb": + sqldb.SetMaxOpenConns(s.maxOpenConns) + sqldb.SetMaxIdleConns(s.maxIdleConns) + sqldb.SetConnMaxLifetime(s.connMaxLifetime) + case "postgres", "postgresql": + sqldb.SetMaxOpenConns(s.maxOpenConns) + sqldb.SetMaxIdleConns(s.maxIdleConns) + sqldb.SetConnMaxLifetime(s.connMaxLifetime) + } + + return nil +} + +// Open simply opens the database connection. +func (s *GormdbStore) Open() error { + dialect, err := s.open() + + if err != nil { + return err + } + + handle, err := gorm.Open( + dialect, + &gorm.Config{ + Logger: NewLogger(), + DisableAutomaticPing: true, + }, + ) + + if err != nil { + return err + } + + s.handle = handle + return s.Prepare() +} + +// Close simply closes the database connection. +func (s *GormdbStore) Close() error { + sqldb, err := s.handle.DB() + + if err != nil { + return err + } + + return sqldb.Close() +} + +// Ping just tests the database connection. +func (s *GormdbStore) Ping() error { + sqldb, err := s.handle.DB() + + if err != nil { + return err + } + + return sqldb.Ping() +} + +// Migrate executes required db migrations. +func (s *GormdbStore) Migrate() error { + migrate := gormigrate.New( + s.handle, + gormigrate.DefaultOptions, + Migrations, + ) + + return migrate.Migrate() +} + +// Admin creates an initial admin user within the database. +func (db *GormdbStore) Admin(username, password, email string) error { + admin := &model.User{} + + if err := db.handle.Where( + &model.User{ + Username: username, + }, + ).First( + admin, + ).Error; err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { + return err + } + + admin.Username = username + admin.Password = password + admin.Email = email + admin.Admin = true + + if admin.Name == "" { + admin.Name = "Admin" + } + + tx := db.handle.Begin() + defer func() { + if r := recover(); r != nil { + tx.Rollback() + } + }() + + if admin.ID == 0 { + if err := db.CreateUser( + context.Background(), + admin, + ); err != nil { + return err + } + } else { + if err := db.UpdateUser( + context.Background(), + admin, + ); err != nil { + return err + } + } + + return nil +} + +func (db *GormdbStore) open() (gorm.Dialector, error) { + switch db.driver { + case "sqlite", "sqlite3": + dsn := fmt.Sprintf( + "%s?%s", + db.database, + db.params.Encode(), + ) + + return sqlite.Open(dsn), nil + case "mysql", "mariadb": + dsn := fmt.Sprintf( + "%s@(%s)/%s?%s", + db.username, + db.hostname, + db.database, + db.params.Encode(), + ) + + if db.password != "" { + dsn = fmt.Sprintf( + "%s:%s@(%s)/%s?%s", + db.username, + db.password, + db.hostname, + db.database, + db.params.Encode(), + ) + } + + return mysql.Open(dsn), nil + case "postgres", "postgresql": + dsn := fmt.Sprintf( + "%s@%s/%s?%s", + db.username, + db.hostname, + db.database, + db.params.Encode(), + ) + + if db.password != "" { + dsn = fmt.Sprintf( + "%s:%s@%s/%s?%s", + db.username, + db.password, + db.hostname, + db.database, + db.params.Encode(), + ) + } + + return postgres.Open(dsn), nil + } + + return nil, nil +} + +func (db *GormdbStore) sortQuery(query *gorm.DB, props db.ObjectProps, params model.RetrieveQueryParams) *gorm.DB { + orderDirection := "ASC" + if params.SortInverted { + orderDirection = "DESC" + } + + orderColumn := props.DefaultSortingColumn + if slices.Contains(props.SortableColumns, params.SortBy) { + orderColumn = params.SortBy + } + + if orderColumn != "" { + query = query.Order(strings.Join( + []string{orderColumn, orderDirection}, + " ", + )) + } + + if params.Count > 0 { + query = query.Limit(params.Count) + } + + if params.Offset > 0 { + query = query.Offset(params.Offset) + } + + return query +} + +// NewStore initializes a new gorm store instance +func NewStore(cfg util.DbConfig) (store.Store, error) { + client := &GormdbStore{ + driver: cfg.Dialect, + database: cfg.DbName, + + username: cfg.Username, + password: cfg.Password, + + params: url.Values{}, + } + + if val, ok := cfg.Options["maxOpenConns"]; ok { + cur, err := strconv.Atoi( + val, + ) + + if err != nil { + return nil, fmt.Errorf("failed to parse maxOpenConns: %w", err) + } + + client.maxOpenConns = cur + delete(cfg.Options, "maxOpenConns") + } else { + client.maxOpenConns = 25 + } + + if val, ok := cfg.Options["maxIdleConns"]; ok { + cur, err := strconv.Atoi( + val, + ) + + if err != nil { + return nil, fmt.Errorf("failed to parse maxIdleConns: %w", err) + } + + client.maxIdleConns = cur + delete(cfg.Options, "maxIdelConns") + } else { + client.maxIdleConns = 25 + } + + if val, ok := cfg.Options["connMaxLifetime"]; ok { + cur, err := time.ParseDuration( + val, + ) + + if err != nil { + return nil, fmt.Errorf("failed to parse connMaxLifetime: %w", err) + } + + client.connMaxLifetime = cur + delete(cfg.Options, "connMaxLifetime") + } else { + client.connMaxLifetime = 5 * time.Minute + } + + for key, val := range cfg.Options { + client.params.Set(key, val) + } + + switch client.driver { + case "sqlite", "sqlite3": + client.driver = "sqlite" + client.database = cfg.Hostname + + client.params.Add("_pragma", "journal_mode(WAL)") + client.params.Add("_pragma", "busy_timeout(5000)") + client.params.Add("_pragma", "foreign_keys(1)") + case "mysql", "mariadb": + client.driver = "mysql" + client.hostname = cfg.Hostname + + if _, ok := cfg.Options["charset"]; !ok { + client.params.Set("charset", "utf8") + } + + if _, ok := cfg.Options["parseTime"]; !ok { + client.params.Set("parseTime", "True") + } + + if _, ok := cfg.Options["loc"]; !ok { + client.params.Set("loc", "Local") + } + case "postgres", "postgresql": + client.driver = "postgres" + client.hostname = cfg.Hostname + + if _, ok := cfg.Options["sslmode"]; !ok { + client.params.Set("sslmode", "disable") + } + } + + return client, nil +} + +// MustStore simply calls NewStore and panics on an error. +func MustStore(cfg util.DbConfig) store.Store { + s, err := NewStore(cfg) + + if err != nil { + panic(err) + } + + return s +} diff --git a/pkg/store/gormdb/integration_aliases.go b/pkg/store/gormdb/integration_aliases.go new file mode 100644 index 000000000..93237c4eb --- /dev/null +++ b/pkg/store/gormdb/integration_aliases.go @@ -0,0 +1,19 @@ +package gormdb + +import ( + "context" + + model "github.com/ansible-semaphore/semaphore/db" +) + +func (db *GormdbStore) ListIntegrationAliases(ctx context.Context, params model.IntegrationAliasParams) ([]*model.IntegrationAlias, error) { + return nil, nil +} + +func (db *GormdbStore) DeleteIntegrationAlias(ctx context.Context, params model.IntegrationAliasParams) error { + return nil +} + +func (db *GormdbStore) CreateIntegrationAlias(ctx context.Context, record *model.IntegrationAlias) error { + return nil +} diff --git a/pkg/store/gormdb/integration_extract_values.go b/pkg/store/gormdb/integration_extract_values.go new file mode 100644 index 000000000..6e5d21fa8 --- /dev/null +++ b/pkg/store/gormdb/integration_extract_values.go @@ -0,0 +1,31 @@ +package gormdb + +import ( + "context" + + model "github.com/ansible-semaphore/semaphore/db" +) + +func (db *GormdbStore) IntegrationExtractValueRefs(ctx context.Context, params model.IntegrationExtractValueParams) (*model.IntegrationExtractorChildReferrers, error) { + return nil, nil +} + +func (db *GormdbStore) ListIntegrationExtractValues(ctx context.Context, params model.IntegrationExtractValueParams) ([]*model.IntegrationExtractValue, error) { + return nil, nil +} + +func (db *GormdbStore) ShowIntegrationExtractValue(ctx context.Context, params model.IntegrationExtractValueParams) (*model.IntegrationExtractValue, error) { + return nil, nil +} + +func (db *GormdbStore) DeleteIntegrationExtractValue(ctx context.Context, params model.IntegrationExtractValueParams) error { + return nil +} + +func (db *GormdbStore) CreateIntegrationExtractValue(ctx context.Context, params model.IntegrationExtractValueParams, record *model.IntegrationExtractValue) error { + return nil +} + +func (db *GormdbStore) UpdateIntegrationExtractValue(ctx context.Context, params model.IntegrationExtractValueParams, record *model.IntegrationExtractValue) error { + return nil +} diff --git a/pkg/store/gormdb/integration_matchers.go b/pkg/store/gormdb/integration_matchers.go new file mode 100644 index 000000000..acb5b4da4 --- /dev/null +++ b/pkg/store/gormdb/integration_matchers.go @@ -0,0 +1,31 @@ +package gormdb + +import ( + "context" + + model "github.com/ansible-semaphore/semaphore/db" +) + +func (db *GormdbStore) IntegrationMatcherRefs(ctx context.Context, params model.IntegrationMatcherParams) (*model.IntegrationExtractorChildReferrers, error) { + return nil, nil +} + +func (db *GormdbStore) ListIntegrationMatchers(ctx context.Context, params model.IntegrationMatcherParams) ([]*model.IntegrationMatcher, error) { + return nil, nil +} + +func (db *GormdbStore) ShowIntegrationMatcher(ctx context.Context, params model.IntegrationMatcherParams) (*model.IntegrationMatcher, error) { + return nil, nil +} + +func (db *GormdbStore) DeleteIntegrationMatcher(ctx context.Context, params model.IntegrationMatcherParams) error { + return nil +} + +func (db *GormdbStore) CreateIntegrationMatcher(ctx context.Context, params model.IntegrationMatcherParams, record *model.IntegrationMatcher) error { + return nil +} + +func (db *GormdbStore) UpdateIntegrationMatcher(ctx context.Context, params model.IntegrationMatcherParams, record *model.IntegrationMatcher) error { + return nil +} diff --git a/pkg/store/gormdb/integrations.go b/pkg/store/gormdb/integrations.go new file mode 100644 index 000000000..830cfe8e8 --- /dev/null +++ b/pkg/store/gormdb/integrations.go @@ -0,0 +1,39 @@ +package gormdb + +import ( + "context" + + model "github.com/ansible-semaphore/semaphore/db" +) + +func (db *GormdbStore) IntegrationRefs(ctx context.Context, params model.IntegrationParams) (*model.IntegrationReferrers, error) { + return nil, nil +} + +func (db *GormdbStore) SearchableIntegrations(ctx context.Context, params model.IntegrationParams) ([]*model.Integration, error) { + return nil, nil +} + +func (db *GormdbStore) AliasedIntegrations(ctx context.Context, params model.IntegrationParams) ([]*model.Integration, error) { + return nil, nil +} + +func (db *GormdbStore) ListIntegrations(ctx context.Context, params model.IntegrationParams) ([]*model.Integration, error) { + return nil, nil +} + +func (db *GormdbStore) ShowIntegration(ctx context.Context, params model.IntegrationParams) (*model.Integration, error) { + return nil, nil +} + +func (db *GormdbStore) DeleteIntegration(ctx context.Context, params model.IntegrationParams) error { + return nil +} + +func (db *GormdbStore) CreateIntegration(ctx context.Context, record *model.Integration) error { + return nil +} + +func (db *GormdbStore) UpdateIntegration(ctx context.Context, record *model.Integration) error { + return nil +} diff --git a/pkg/store/gormdb/inventories.go b/pkg/store/gormdb/inventories.go new file mode 100644 index 000000000..e835ae90d --- /dev/null +++ b/pkg/store/gormdb/inventories.go @@ -0,0 +1,31 @@ +package gormdb + +import ( + "context" + + model "github.com/ansible-semaphore/semaphore/db" +) + +func (db *GormdbStore) InventoryRefs(ctx context.Context, params model.InventoryParams) (*model.ObjectReferrers, error) { + return nil, nil +} + +func (db *GormdbStore) ListInventories(ctx context.Context, params model.InventoryParams) ([]*model.Inventory, error) { + return nil, nil +} + +func (db *GormdbStore) ShowInventory(ctx context.Context, params model.InventoryParams) (*model.Inventory, error) { + return nil, nil +} + +func (db *GormdbStore) DeleteInventory(ctx context.Context, params model.InventoryParams) error { + return nil +} + +func (db *GormdbStore) CreateInventory(ctx context.Context, record *model.Inventory) error { + return nil +} + +func (db *GormdbStore) UpdateInventory(ctx context.Context, record *model.Inventory) error { + return nil +} diff --git a/pkg/store/gormdb/logger.go b/pkg/store/gormdb/logger.go new file mode 100644 index 000000000..a2e3385f2 --- /dev/null +++ b/pkg/store/gormdb/logger.go @@ -0,0 +1,69 @@ +package gormdb + +import ( + "context" + "errors" + "time" + + log "github.com/sirupsen/logrus" + "gorm.io/gorm" + gormlogger "gorm.io/gorm/logger" + "gorm.io/gorm/utils" +) + +type Logger struct { + SlowThreshold time.Duration + SkipErrRecordNotFound bool + Debug bool + SourceField string +} + +func (l *Logger) LogMode(gormlogger.LogLevel) gormlogger.Interface { + return l +} + +func (l *Logger) Info(ctx context.Context, s string, args ...interface{}) { + log.WithContext(ctx).Infof(s, args) +} + +func (l *Logger) Warn(ctx context.Context, s string, args ...interface{}) { + log.WithContext(ctx).Warnf(s, args) +} + +func (l *Logger) Error(ctx context.Context, s string, args ...interface{}) { + log.WithContext(ctx).Errorf(s, args) +} + +func (l *Logger) Trace(ctx context.Context, begin time.Time, fc func() (string, int64), err error) { + elapsed := time.Since(begin) + sql, _ := fc() + fields := log.Fields{} + + if l.SourceField != "" { + fields[l.SourceField] = utils.FileWithLineNum() + } + + if err != nil && !(errors.Is(err, gorm.ErrRecordNotFound) && l.SkipErrRecordNotFound) { + fields[log.ErrorKey] = err + + log.WithContext(ctx).WithFields(fields).Errorf("%s [%s]", sql, elapsed) + return + } + + if l.SlowThreshold != 0 && elapsed > l.SlowThreshold { + log.WithContext(ctx).WithFields(fields).Warnf("%s [%s]", sql, elapsed) + return + } + + if l.Debug { + log.WithContext(ctx).WithFields(fields).Debugf("%s [%s]", sql, elapsed) + } +} + +func NewLogger() *Logger { + return &Logger{ + SlowThreshold: 200 * time.Millisecond, + SkipErrRecordNotFound: true, + Debug: true, + } +} diff --git a/pkg/store/gormdb/members.go b/pkg/store/gormdb/members.go new file mode 100644 index 000000000..337dae67d --- /dev/null +++ b/pkg/store/gormdb/members.go @@ -0,0 +1,27 @@ +package gormdb + +import ( + "context" + + model "github.com/ansible-semaphore/semaphore/db" +) + +func (db *GormdbStore) ListMembers(ctx context.Context, params model.MemberParams) ([]*model.UserWithProjectRole, error) { + return nil, nil +} + +func (db *GormdbStore) ShowMember(ctx context.Context, params model.MemberParams) (*model.ProjectUser, error) { + return nil, nil +} + +func (db *GormdbStore) DeleteMember(ctx context.Context, params model.MemberParams) error { + return nil +} + +func (db *GormdbStore) CreateMember(ctx context.Context, record *model.ProjectUser) error { + return nil +} + +func (db *GormdbStore) UpdateMember(ctx context.Context, record *model.ProjectUser) error { + return nil +} diff --git a/pkg/store/gormdb/migrate.go b/pkg/store/gormdb/migrate.go new file mode 100644 index 000000000..fffa81a1b --- /dev/null +++ b/pkg/store/gormdb/migrate.go @@ -0,0 +1,125 @@ +package gormdb + +import ( + "github.com/go-gormigrate/gormigrate/v2" +) + +var ( + // Migrations define all database migrations. + Migrations = []*gormigrate.Migration{ + // { + // ID: "20240306_create_users_table", + // Migrate: func(tx *gorm.DB) error { + // type User struct { + // ID string `gorm:"primaryKey;length:36"` + // Slug string `gorm:"unique;length:255"` + // Username string `gorm:"unique;length:255"` + // Hashword string `gorm:"length:255"` + // Email string `gorm:"unique;length:255"` + // Fullname string `gorm:"length:255"` + // Active bool `gorm:"default:false"` + // Admin bool `gorm:"default:false"` + // CreatedAt time.Time + // UpdatedAt time.Time + // } + + // return tx.Migrator().CreateTable(&User{}) + // }, + // Rollback: func(tx *gorm.DB) error { + // return tx.Migrator().DropTable("users") + // }, + // }, + // { + // ID: "20240306_create_teams_Table", + // Migrate: func(tx *gorm.DB) error { + // type Team struct { + // ID string `gorm:"primaryKey;length:36"` + // Slug string `gorm:"unique;length:255"` + // Name string `gorm:"unique;length:255"` + // CreatedAt time.Time + // UpdatedAt time.Time + // } + + // return tx.Migrator().CreateTable(&Team{}) + // }, + // Rollback: func(tx *gorm.DB) error { + // return tx.Migrator().DropTable("teams") + // }, + // }, + // { + // ID: "20240306_create_members_table", + // Migrate: func(tx *gorm.DB) error { + // type Member struct { + // TeamID string `gorm:"index:idx_id,unique;length:36"` + // UserID string `gorm:"index:idx_id,unique;length:36"` + // Perm string `gorm:"length:255"` + // CreatedAt time.Time + // UpdatedAt time.Time + // } + + // return tx.Migrator().CreateTable(&Member{}) + // }, + // Rollback: func(tx *gorm.DB) error { + // return tx.Migrator().DropTable("members") + // }, + // }, + // { + // ID: "20240306_create_members_teams_constraint", + // Migrate: func(tx *gorm.DB) error { + // type Member struct { + // TeamID string `gorm:"index:idx_id,unique;length:36"` + // UserID string `gorm:"index:idx_id,unique;length:36"` + // } + + // type Team struct { + // ID string `gorm:"primaryKey"` + // Users []*Member `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"` + // } + + // return tx.Migrator().CreateConstraint(&Team{}, "Users") + // }, + // Rollback: func(tx *gorm.DB) error { + // type Member struct { + // TeamID string `gorm:"index:idx_id,unique;length:36"` + // UserID string `gorm:"index:idx_id,unique;length:36"` + // } + + // type Team struct { + // ID string `gorm:"primaryKey"` + // Users []*Member `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"` + // } + + // return tx.Migrator().DropConstraint(&Team{}, "Users") + // }, + // }, + // { + // ID: "20240306_create_members_users_constraint", + // Migrate: func(tx *gorm.DB) error { + // type Member struct { + // TeamID string `gorm:"index:idx_id,unique;length:36"` + // UserID string `gorm:"index:idx_id,unique;length:36"` + // } + + // type User struct { + // ID string `gorm:"primaryKey"` + // Teams []*Member `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"` + // } + + // return tx.Migrator().CreateConstraint(&User{}, "Teams") + // }, + // Rollback: func(tx *gorm.DB) error { + // type Member struct { + // TeamID string `gorm:"index:idx_id,unique;length:36"` + // UserID string `gorm:"index:idx_id,unique;length:36"` + // } + + // type User struct { + // ID string `gorm:"primaryKey"` + // Teams []*Member `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"` + // } + + // return tx.Migrator().DropConstraint(&User{}, "Teams") + // }, + // }, + } +) diff --git a/pkg/store/gormdb/options.go b/pkg/store/gormdb/options.go new file mode 100644 index 000000000..38d053562 --- /dev/null +++ b/pkg/store/gormdb/options.go @@ -0,0 +1,13 @@ +package gormdb + +import ( + "context" +) + +func (db *GormdbStore) GetOption(ctx context.Context, key string) error { + return nil +} + +func (db *GormdbStore) SetOption(ctx context.Context, key, val string) error { + return nil +} diff --git a/pkg/store/gormdb/projects.go b/pkg/store/gormdb/projects.go new file mode 100644 index 000000000..b5f934d5d --- /dev/null +++ b/pkg/store/gormdb/projects.go @@ -0,0 +1,27 @@ +package gormdb + +import ( + "context" + + model "github.com/ansible-semaphore/semaphore/db" +) + +func (db *GormdbStore) ListProjects(ctx context.Context, params model.ProjectParams) ([]*model.Project, error) { + return nil, nil +} + +func (db *GormdbStore) ShowProject(ctx context.Context, params model.ProjectParams) (*model.Project, error) { + return nil, nil +} + +func (db *GormdbStore) DeleteProject(ctx context.Context, params model.ProjectParams) error { + return nil +} + +func (db *GormdbStore) CreateProject(ctx context.Context, record *model.Project) error { + return nil +} + +func (db *GormdbStore) UpdateProject(ctx context.Context, record *model.Project) error { + return nil +} diff --git a/pkg/store/gormdb/repos.go b/pkg/store/gormdb/repos.go new file mode 100644 index 000000000..007f2cb6c --- /dev/null +++ b/pkg/store/gormdb/repos.go @@ -0,0 +1,31 @@ +package gormdb + +import ( + "context" + + model "github.com/ansible-semaphore/semaphore/db" +) + +func (db *GormdbStore) RepoRefs(ctx context.Context, params model.RepoParams) (*model.ObjectReferrers, error) { + return nil, nil +} + +func (db *GormdbStore) ListRepos(ctx context.Context, params model.RepoParams) ([]*model.Repository, error) { + return nil, nil +} + +func (db *GormdbStore) ShowRepo(ctx context.Context, params model.RepoParams) (*model.Repository, error) { + return nil, nil +} + +func (db *GormdbStore) DeleteRepo(ctx context.Context, params model.RepoParams) error { + return nil +} + +func (db *GormdbStore) CreateRepo(ctx context.Context, record *model.Repository) error { + return nil +} + +func (db *GormdbStore) UpdateRepo(ctx context.Context, record *model.Repository) error { + return nil +} diff --git a/pkg/store/gormdb/runners.go b/pkg/store/gormdb/runners.go new file mode 100644 index 000000000..a814b4e8a --- /dev/null +++ b/pkg/store/gormdb/runners.go @@ -0,0 +1,160 @@ +package gormdb + +import ( + "context" + + model "github.com/ansible-semaphore/semaphore/db" + "github.com/ansible-semaphore/semaphore/pkg/store" + "gorm.io/gorm" +) + +func (db *GormdbStore) ListGlobalRunners(ctx context.Context, params model.RunnerParams) ([]*model.Runner, error) { + records := make([]*model.Runner, 0) + + err := db.handle.WithContext(ctx).Order( + "id ASC", + ).Find( + &records, + ).Error + + return records, err +} + +func (db *GormdbStore) ShowGlobalRunner(ctx context.Context, params model.RunnerParams) (*model.Runner, error) { + record := &model.Runner{} + + err := db.handle.WithContext(ctx).Where( + "id = ?", + params.RunnerID, + ).First( + record, + ).Error + + if err == gorm.ErrRecordNotFound { + return record, store.ErrRecordNotFound + } + + return record, err +} + +func (db *GormdbStore) DeleteGlobalRunner(ctx context.Context, params model.RunnerParams) error { + tx := db.handle.WithContext(ctx).Begin() + defer tx.Rollback() + + if err := tx.Where( + "id = ?", + params.RunnerID, + ).Delete( + &model.Runner{}, + ).Error; err != nil { + if err == gorm.ErrRecordNotFound { + return store.ErrRecordNotFound + } + + return err + } + + return tx.Commit().Error +} + +func (db *GormdbStore) CreateGlobalRunner(ctx context.Context, record *model.Runner) error { + tx := db.handle.WithContext(ctx).Begin() + defer tx.Rollback() + + if err := tx.Create(record).Error; err != nil { + return err + } + + return tx.Commit().Error +} + +func (db *GormdbStore) UpdateGlobalRunner(ctx context.Context, record *model.Runner) error { + tx := db.handle.WithContext(ctx).Begin() + defer tx.Rollback() + + if err := tx.Save(record).Error; err != nil { + return err + } + + return tx.Commit().Error +} + +func (db *GormdbStore) ListProjectRunner(ctx context.Context, params model.RunnerParams) ([]*model.Runner, error) { + records := make([]*model.Runner, 0) + + err := db.handle.WithContext(ctx).Order( + "id ASC", + ).Where( + "project_id = ?", + params.ProjectID, + ).Find( + &records, + ).Error + + return records, err +} + +func (db *GormdbStore) ShowProjectRunner(ctx context.Context, params model.RunnerParams) (*model.Runner, error) { + record := &model.Runner{} + + err := db.handle.WithContext(ctx).Where( + "id = ?", + params.RunnerID, + ).Where( + "project_id = ?", + params.ProjectID, + ).First( + record, + ).Error + + if err == gorm.ErrRecordNotFound { + return record, store.ErrRecordNotFound + } + + return record, err +} + +func (db *GormdbStore) DeleteProjectRunner(ctx context.Context, params model.RunnerParams) error { + tx := db.handle.WithContext(ctx).Begin() + defer tx.Rollback() + + if err := tx.Where( + "id = ?", + params.RunnerID, + ).Where( + "project_id = ?", + params.ProjectID, + ).Delete( + &model.Runner{}, + ).Error; err != nil { + if err == gorm.ErrRecordNotFound { + return store.ErrRecordNotFound + } + + return err + } + + return tx.Commit().Error +} + +func (db *GormdbStore) CreateProjectRunner(ctx context.Context, record *model.Runner) error { + tx := db.handle.WithContext(ctx).Begin() + defer tx.Rollback() + + if err := tx.Create(record).Error; err != nil { + return err + } + + return tx.Commit().Error +} + +func (db *GormdbStore) UpdateProjectRunner(ctx context.Context, record *model.Runner) error { + tx := db.handle.WithContext(ctx).Begin() + defer tx.Rollback() + + if err := tx.Save(record).Error; err != nil { + return err + } + + return tx.Commit().Error +} diff --git a/pkg/store/gormdb/schedules.go b/pkg/store/gormdb/schedules.go new file mode 100644 index 000000000..f96c880d5 --- /dev/null +++ b/pkg/store/gormdb/schedules.go @@ -0,0 +1,35 @@ +package gormdb + +import ( + "context" + + model "github.com/ansible-semaphore/semaphore/db" +) + +func (db *GormdbStore) TemplateSchedules(ctx context.Context, params model.ScheduleParams) ([]*model.Schedule, error) { + return nil, nil +} + +func (db *GormdbStore) ListSchedules(ctx context.Context, params model.ScheduleParams) ([]*model.Schedule, error) { + return nil, nil +} + +func (db *GormdbStore) ShowSchedule(ctx context.Context, params model.ScheduleParams) (*model.Schedule, error) { + return nil, nil +} + +func (db *GormdbStore) DeleteSchedule(ctx context.Context, params model.ScheduleParams) error { + return nil +} + +func (db *GormdbStore) HashSchedule(ctx context.Context, params model.ScheduleParams) error { + return nil +} + +func (db *GormdbStore) CreateSchedule(ctx context.Context, record *model.Schedule) error { + return nil +} + +func (db *GormdbStore) UpdateSchedule(ctx context.Context, record *model.Schedule) error { + return nil +} diff --git a/pkg/store/gormdb/sessions.go b/pkg/store/gormdb/sessions.go new file mode 100644 index 000000000..2ee4edb44 --- /dev/null +++ b/pkg/store/gormdb/sessions.go @@ -0,0 +1,23 @@ +package gormdb + +import ( + "context" + + model "github.com/ansible-semaphore/semaphore/db" +) + +func (db *GormdbStore) ShowSession(ctx context.Context, params model.SessionParams) (*model.Session, error) { + return nil, nil +} + +func (db *GormdbStore) ExpireSession(ctx context.Context, params model.SessionParams) error { + return nil +} + +func (db *GormdbStore) TouchSession(ctx context.Context, params model.SessionParams) error { + return nil +} + +func (db *GormdbStore) CreateSession(ctx context.Context, record *model.Session) error { + return nil +} diff --git a/pkg/store/gormdb/tasks.go b/pkg/store/gormdb/tasks.go new file mode 100644 index 000000000..a52ed687b --- /dev/null +++ b/pkg/store/gormdb/tasks.go @@ -0,0 +1,39 @@ +package gormdb + +import ( + "context" + + model "github.com/ansible-semaphore/semaphore/db" +) + +func (db *GormdbStore) TemplateTasks(ctx context.Context, params model.TaskParams) ([]*model.TaskWithTpl, error) { + return nil, nil +} + +func (db *GormdbStore) ProjectTasks(ctx context.Context, params model.TaskParams) ([]*model.TaskWithTpl, error) { + return nil, nil +} + +func (db *GormdbStore) ShowTask(ctx context.Context, params model.TaskParams) (*model.Task, error) { + return nil, nil +} + +func (db *GormdbStore) DeleteTask(ctx context.Context, params model.TaskParams) error { + return nil +} + +func (db *GormdbStore) CreateTask(ctx context.Context, record *model.Task) error { + return nil +} + +func (db *GormdbStore) UpdateTask(ctx context.Context, record *model.Task) error { + return nil +} + +func (db *GormdbStore) PushOutput(ctx context.Context, record *model.TaskOutput) error { + return nil +} + +func (db *GormdbStore) GetOutputs(ctx context.Context, params model.TaskParams) ([]*model.TaskOutput, error) { + return nil, nil +} diff --git a/pkg/store/gormdb/templates.go b/pkg/store/gormdb/templates.go new file mode 100644 index 000000000..1e40c2a9f --- /dev/null +++ b/pkg/store/gormdb/templates.go @@ -0,0 +1,31 @@ +package gormdb + +import ( + "context" + + model "github.com/ansible-semaphore/semaphore/db" +) + +func (db *GormdbStore) TemplateRefs(ctx context.Context, params model.TemplateParams) (*model.ObjectReferrers, error) { + return nil, nil +} + +func (db *GormdbStore) ListTemplates(ctx context.Context, params model.TemplateParams) ([]*model.Template, error) { + return nil, nil +} + +func (db *GormdbStore) ShowTemplate(ctx context.Context, params model.TemplateParams) (*model.Template, error) { + return nil, nil +} + +func (db *GormdbStore) DeleteTemplate(ctx context.Context, params model.TemplateParams) error { + return nil +} + +func (db *GormdbStore) CreateTemplate(ctx context.Context, record *model.Template) error { + return nil +} + +func (db *GormdbStore) UpdateTemplate(ctx context.Context, record *model.Template) error { + return nil +} diff --git a/pkg/store/gormdb/tokens.go b/pkg/store/gormdb/tokens.go new file mode 100644 index 000000000..fffc8a01c --- /dev/null +++ b/pkg/store/gormdb/tokens.go @@ -0,0 +1,27 @@ +package gormdb + +import ( + "context" + + model "github.com/ansible-semaphore/semaphore/db" +) + +func (db *GormdbStore) ListTokens(ctx context.Context, params model.TokenParams) ([]*model.APIToken, error) { + return nil, nil +} + +func (db *GormdbStore) ShowToken(ctx context.Context, params model.TokenParams) (*model.APIToken, error) { + return nil, nil +} + +func (db *GormdbStore) DeleteToken(ctx context.Context, params model.TokenParams) error { + return nil +} + +func (db *GormdbStore) ExpireToken(ctx context.Context, params model.TokenParams) error { + return nil +} + +func (db *GormdbStore) CreateToken(ctx context.Context, record *model.APIToken) error { + return nil +} diff --git a/pkg/store/gormdb/users.go b/pkg/store/gormdb/users.go new file mode 100644 index 000000000..492c5a4e4 --- /dev/null +++ b/pkg/store/gormdb/users.go @@ -0,0 +1,35 @@ +package gormdb + +import ( + "context" + + model "github.com/ansible-semaphore/semaphore/db" +) + +func (db *GormdbStore) AdminUsers(ctxy context.Context, params model.UserParams) ([]*model.User, error) { + return nil, nil +} + +func (db *GormdbStore) ListUsers(ctx context.Context, params model.UserParams) ([]*model.User, error) { + return nil, nil +} + +func (db *GormdbStore) ShowUser(ctx context.Context, params model.UserParams) (*model.User, error) { + return nil, nil +} + +func (db *GormdbStore) DeleteUser(ctx context.Context, params model.UserParams) error { + return nil +} + +func (db *GormdbStore) CreateUser(ctx context.Context, record *model.User) error { + return nil +} + +func (db *GormdbStore) UpdateUser(ctx context.Context, record *model.User) error { + return nil +} + +func (db *GormdbStore) UpdatePassword(ctx context.Context, params model.UserParams) error { + return nil +} diff --git a/pkg/store/gormdb/views.go b/pkg/store/gormdb/views.go new file mode 100644 index 000000000..86ea0a447 --- /dev/null +++ b/pkg/store/gormdb/views.go @@ -0,0 +1,31 @@ +package gormdb + +import ( + "context" + + model "github.com/ansible-semaphore/semaphore/db" +) + +func (db *GormdbStore) ListViews(ctx context.Context, params model.ViewParams) ([]*model.View, error) { + return nil, nil +} + +func (db *GormdbStore) ShowView(ctx context.Context, params model.ViewParams) (*model.View, error) { + return nil, nil +} + +func (db *GormdbStore) DeleteView(ctx context.Context, params model.ViewParams) error { + return nil +} + +func (db *GormdbStore) PositionView(ctx context.Context, params model.ViewParams) error { + return nil +} + +func (db *GormdbStore) CreateView(ctx context.Context, record *model.View) error { + return nil +} + +func (db *GormdbStore) UpdateView(ctx context.Context, record *model.View) error { + return nil +} diff --git a/pkg/store/store.go b/pkg/store/store.go new file mode 100644 index 000000000..4bb46d780 --- /dev/null +++ b/pkg/store/store.go @@ -0,0 +1,165 @@ +package store + +import ( + "context" + "fmt" + + model "github.com/ansible-semaphore/semaphore/db" +) + +var ( + // ErrUnknownDriver defines a named error for unknown store drivers. + ErrUnknownDriver = fmt.Errorf("unknown database driver") + + // ErrRecordNotFound is returned when a record was not found. + ErrRecordNotFound = fmt.Errorf("record not found") +) + +// Store provides the interface for the store implementations. +type Store interface { + Info() map[string]interface{} + Prepare() error + Open() error + Close() error + Ping() error + Migrate() error + Admin(string, string, string) error + + GetOption(ctx context.Context, key string) error + SetOption(ctx context.Context, key, val string) error + + UserEvents(ctx context.Context, params model.EventParams) ([]*model.Event, error) + ProjectEvents(ctx context.Context, params model.EventParams) ([]*model.Event, error) + CreateEvent(ctx context.Context, record *model.Event) error + + AdminUsers(ctx context.Context, params model.UserParams) ([]*model.User, error) + ListUsers(Usersctx context.Context, params model.UserParams) ([]*model.User, error) + ShowUser(ctx context.Context, params model.UserParams) (*model.User, error) + DeleteUser(ctx context.Context, params model.UserParams) error + CreateUser(ctx context.Context, record *model.User) error + UpdateUser(ctx context.Context, record *model.User) error + UpdatePassword(ctx context.Context, params model.UserParams) error + + ListTokens(ctx context.Context, params model.TokenParams) ([]*model.APIToken, error) + ShowToken(ctx context.Context, params model.TokenParams) (*model.APIToken, error) + DeleteToken(ctx context.Context, params model.TokenParams) error + ExpireToken(ctx context.Context, params model.TokenParams) error + CreateToken(ctx context.Context, record *model.APIToken) error + + ShowSession(ctx context.Context, params model.SessionParams) (*model.Session, error) + ExpireSession(ctx context.Context, params model.SessionParams) error + TouchSession(ctx context.Context, params model.SessionParams) error + CreateSession(ctx context.Context, record *model.Session) error + + ListGlobalRunners(ctx context.Context, params model.RunnerParams) ([]*model.Runner, error) + ShowGlobalRunner(ctx context.Context, params model.RunnerParams) (*model.Runner, error) + DeleteGlobalRunner(ctx context.Context, params model.RunnerParams) error + CreateGlobalRunner(ctx context.Context, record *model.Runner) error + UpdateGlobalRunner(ctx context.Context, record *model.Runner) error + + ListProjectRunner(ctx context.Context, params model.RunnerParams) ([]*model.Runner, error) + ShowProjectRunner(ctx context.Context, params model.RunnerParams) (*model.Runner, error) + DeleteProjectRunner(ctx context.Context, params model.RunnerParams) error + CreateProjectRunner(ctx context.Context, record *model.Runner) error + UpdateProjectRunner(ctx context.Context, record *model.Runner) error + + ListProjects(ctx context.Context, params model.ProjectParams) ([]*model.Project, error) + ShowProject(ctx context.Context, params model.ProjectParams) (*model.Project, error) + DeleteProject(ctx context.Context, params model.ProjectParams) error + CreateProject(ctx context.Context, record *model.Project) error + UpdateProject(ctx context.Context, record *model.Project) error + + ListMembers(ctx context.Context, params model.MemberParams) ([]*model.UserWithProjectRole, error) + ShowMember(ctx context.Context, params model.MemberParams) (*model.ProjectUser, error) + DeleteMember(ctx context.Context, params model.MemberParams) error + CreateMember(ctx context.Context, record *model.ProjectUser) error + UpdateMember(ctx context.Context, record *model.ProjectUser) error + + TemplateRefs(ctx context.Context, params model.TemplateParams) (*model.ObjectReferrers, error) + ListTemplates(ctx context.Context, params model.TemplateParams) ([]*model.Template, error) + ShowTemplate(ctx context.Context, params model.TemplateParams) (*model.Template, error) + DeleteTemplate(ctx context.Context, params model.TemplateParams) error + CreateTemplate(ctx context.Context, record *model.Template) error + UpdateTemplate(ctx context.Context, record *model.Template) error + + AccessKeyRefs(ctx context.Context, params model.AccessKeyParams) (*model.ObjectReferrers, error) + ListAccessKeys(ctx context.Context, params model.AccessKeyParams) ([]*model.AccessKey, error) + ShowAccessKey(ctx context.Context, params model.AccessKeyParams) (*model.AccessKey, error) + DeleteAccessKey(ctx context.Context, params model.AccessKeyParams) error + RekeyAccessKey(ctx context.Context, params model.AccessKeyParams) error + CreateAccessKey(ctx context.Context, record *model.AccessKey) error + UpdateAccessKey(ctx context.Context, record *model.AccessKey) error + + EnvRefs(ctx context.Context, params model.EnvParams) (*model.ObjectReferrers, error) + ListEnvs(ctx context.Context, params model.EnvParams) ([]*model.Environment, error) + ShowEnv(ctx context.Context, params model.EnvParams) (*model.Environment, error) + DeleteEnv(ctx context.Context, params model.EnvParams) error + CreateEnv(ctx context.Context, record *model.Environment) error + UpdateEnv(ctx context.Context, record *model.Environment) error + + InventoryRefs(ctx context.Context, params model.InventoryParams) (*model.ObjectReferrers, error) + ListInventories(ctx context.Context, params model.InventoryParams) ([]*model.Inventory, error) + ShowInventory(ctx context.Context, params model.InventoryParams) (*model.Inventory, error) + DeleteInventory(ctx context.Context, params model.InventoryParams) error + CreateInventory(ctx context.Context, record *model.Inventory) error + UpdateInventory(ctx context.Context, record *model.Inventory) error + + RepoRefs(ctx context.Context, params model.RepoParams) (*model.ObjectReferrers, error) + ListRepos(ctx context.Context, params model.RepoParams) ([]*model.Repository, error) + ShowRepo(ctx context.Context, params model.RepoParams) (*model.Repository, error) + DeleteRepo(ctx context.Context, params model.RepoParams) error + CreateRepo(ctx context.Context, record *model.Repository) error + UpdateRepo(ctx context.Context, record *model.Repository) error + + ListViews(ctx context.Context, params model.ViewParams) ([]*model.View, error) + ShowView(ctx context.Context, params model.ViewParams) (*model.View, error) + PositionView(ctx context.Context, params model.ViewParams) error + DeleteView(ctx context.Context, params model.ViewParams) error + CreateView(ctx context.Context, record *model.View) error + UpdateView(ctx context.Context, record *model.View) error + + TemplateSchedules(ctx context.Context, params model.ScheduleParams) ([]*model.Schedule, error) + ListSchedules(ctx context.Context, params model.ScheduleParams) ([]*model.Schedule, error) + ShowSchedule(ctx context.Context, params model.ScheduleParams) (*model.Schedule, error) + HashSchedule(ctx context.Context, params model.ScheduleParams) error + DeleteSchedule(ctx context.Context, params model.ScheduleParams) error + CreateSchedule(ctx context.Context, record *model.Schedule) error + UpdateSchedule(ctx context.Context, record *model.Schedule) error + + TemplateTasks(ctx context.Context, params model.TaskParams) ([]*model.TaskWithTpl, error) + ProjectTasks(ctx context.Context, params model.TaskParams) ([]*model.TaskWithTpl, error) + ShowTask(ctx context.Context, params model.TaskParams) (*model.Task, error) + DeleteTask(ctx context.Context, params model.TaskParams) error + CreateTask(ctx context.Context, record *model.Task) error + UpdateTask(ctx context.Context, record *model.Task) error + + PushOutput(ctx context.Context, record *model.TaskOutput) error + GetOutputs(ctx context.Context, params model.TaskParams) ([]*model.TaskOutput, error) + + IntegrationRefs(ctx context.Context, params model.IntegrationParams) (*model.IntegrationReferrers, error) + SearchableIntegrations(ctx context.Context, params model.IntegrationParams) ([]*model.Integration, error) + AliasedIntegrations(ctx context.Context, params model.IntegrationParams) ([]*model.Integration, error) + ListIntegrations(ctx context.Context, params model.IntegrationParams) ([]*model.Integration, error) + ShowIntegration(ctx context.Context, params model.IntegrationParams) (*model.Integration, error) + DeleteIntegration(ctx context.Context, params model.IntegrationParams) error + CreateIntegration(ctx context.Context, record *model.Integration) error + UpdateIntegration(ctx context.Context, record *model.Integration) error + + IntegrationExtractValueRefs(ctx context.Context, params model.IntegrationExtractValueParams) (*model.IntegrationExtractorChildReferrers, error) + ListIntegrationExtractValues(ctx context.Context, params model.IntegrationExtractValueParams) ([]*model.IntegrationExtractValue, error) + ShowIntegrationExtractValue(ctx context.Context, params model.IntegrationExtractValueParams) (*model.IntegrationExtractValue, error) + DeleteIntegrationExtractValue(ctx context.Context, params model.IntegrationExtractValueParams) error + CreateIntegrationExtractValue(ctx context.Context, params model.IntegrationExtractValueParams, record *model.IntegrationExtractValue) error + UpdateIntegrationExtractValue(ctx context.Context, params model.IntegrationExtractValueParams, record *model.IntegrationExtractValue) error + + IntegrationMatcherRefs(ctx context.Context, params model.IntegrationMatcherParams) (*model.IntegrationExtractorChildReferrers, error) + ListIntegrationMatchers(ctx context.Context, params model.IntegrationMatcherParams) ([]*model.IntegrationMatcher, error) + ShowIntegrationMatcher(ctx context.Context, params model.IntegrationMatcherParams) (*model.IntegrationMatcher, error) + DeleteIntegrationMatcher(ctx context.Context, params model.IntegrationMatcherParams) error + CreateIntegrationMatcher(ctx context.Context, params model.IntegrationMatcherParams, record *model.IntegrationMatcher) error + UpdateIntegrationMatcher(ctx context.Context, params model.IntegrationMatcherParams, record *model.IntegrationMatcher) error + + ListIntegrationAliases(ctx context.Context, params model.IntegrationAliasParams) ([]*model.IntegrationAlias, error) + DeleteIntegrationAlias(ctx context.Context, params model.IntegrationAliasParams) error + CreateIntegrationAlias(ctx context.Context, record *model.IntegrationAlias) error +}