Skip to content

Commit

Permalink
feat: Complete clients api.
Browse files Browse the repository at this point in the history
  • Loading branch information
gfyrag committed Aug 2, 2022
1 parent 79396de commit cc82aaa
Show file tree
Hide file tree
Showing 10 changed files with 607 additions and 61 deletions.
17 changes: 12 additions & 5 deletions cmd/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"fmt"
"strings"

"github.com/google/uuid"
auth "github.com/numary/auth/pkg"
"github.com/numary/auth/pkg/api"
"github.com/numary/auth/pkg/delegatedauth"
Expand Down Expand Up @@ -99,11 +100,17 @@ var serveCmd = &cobra.Command{
"post_logout_redirect_uri": `["http://localhost:3000/"]`,
"scopes": fmt.Sprintf(`["%s"]`, strings.Join(auth.Scopes, `", "`)),
"access_token_type": op.AccessTokenTypeJWT,
"secrets": `[{"value": "1234"}]`,
}),
}).
Create(&auth.Client{
Id: "demo",
Secret: "1234",
Id: "demo",
Secrets: auth.Array[auth.ClientSecret]{
{
ID: uuid.NewString(),
Hash: "1234",
},
},
RedirectURIs: auth.Array[string]{
"http://localhost:3000/auth-callback",
},
Expand All @@ -115,9 +122,9 @@ var serveCmd = &cobra.Command{
oidc.GrantTypeRefreshToken,
oidc.GrantTypeClientCredentials,
},
AccessTokenType: op.AccessTokenTypeJWT,
PostLogoutRedirectUri: auth.Array[string]{"http://localhost:3000/"},
Scopes: auth.Scopes,
AccessTokenType: op.AccessTokenTypeJWT,
PostLogoutRedirectUris: auth.Array[string]{"http://localhost:3000/"},
Scopes: auth.Scopes,
}).Error
},
})
Expand Down
6 changes: 5 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -44,17 +44,20 @@ require (
github.com/jackc/pgtype v1.11.0 // indirect
github.com/jackc/pgx/v4 v4.16.1 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.4 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/lib/pq v1.10.6 // indirect
github.com/magiconair/properties v1.8.6 // indirect
github.com/mattn/go-sqlite3 v1.14.12 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/pelletier/go-toml v1.9.5 // indirect
github.com/pelletier/go-toml/v2 v2.0.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/pquerna/cachecontrol v0.1.0 // indirect
github.com/spf13/afero v1.8.2 // indirect
github.com/spf13/cast v1.5.0 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/stretchr/testify v1.8.0 // indirect
github.com/subosito/gotenv v1.3.0 // indirect
github.com/zitadel/logging v0.3.4 // indirect
go.opentelemetry.io/contrib/instrumentation/github.com/gorilla/mux/otelmux v0.33.0 // indirect
Expand Down Expand Up @@ -82,4 +85,5 @@ require (
gopkg.in/ini.v1 v1.66.4 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
gorm.io/driver/sqlite v1.3.6 // indirect
)
7 changes: 7 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,8 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.4 h1:tHnRBy1i5F2Dh8BAFxqFzxKqqvezXrL2OW1TnX+Mlas=
github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
Expand All @@ -266,6 +268,8 @@ github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-sqlite3 v1.14.12 h1:TJ1bhYJPV44phC+IMu1u2K/i5RriLTPe+yc68XDJ1Z0=
github.com/mattn/go-sqlite3 v1.14.12/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
Expand Down Expand Up @@ -758,6 +762,9 @@ 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/postgres v1.3.8 h1:8bEphSAB69t3odsCR4NDzt581iZEWQuRM27Cg6KgfPY=
gorm.io/driver/postgres v1.3.8/go.mod h1:qB98Aj6AhRO/oyu/jmZsi/YM9g6UzVCjMxO/6frFvcA=
gorm.io/driver/sqlite v1.3.6 h1:Fi8xNYCUplOqWiPa3/GuCeowRNBRGTf62DEmhMDHeQQ=
gorm.io/driver/sqlite v1.3.6/go.mod h1:Sg1/pvnKtbQ7jLXxfZa+jSHvoX8hoZA8cn4xllOMTgE=
gorm.io/gorm v1.23.4/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
gorm.io/gorm v1.23.6/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
gorm.io/gorm v1.23.8 h1:h8sGJ+biDgBA1AD1Ha9gFCx7h8npU7AsLdlkX0n2TpE=
gorm.io/gorm v1.23.8/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
Expand Down
211 changes: 195 additions & 16 deletions pkg/api/clients.go
Original file line number Diff line number Diff line change
@@ -1,37 +1,216 @@
package api

import (
"encoding/json"
"net/http"

"github.com/gorilla/mux"
auth "github.com/numary/auth/pkg"
"github.com/numary/go-libs/sharedapi"
_ "github.com/numary/go-libs/sharedapi"
"github.com/numary/go-libs/sharedlogging"
"github.com/zitadel/oidc/pkg/oidc"
"go.opentelemetry.io/otel/trace"
"gorm.io/gorm"
)

func deleteSecret(writer http.ResponseWriter, request *http.Request) {
writer.WriteHeader(http.StatusNotImplemented)
func validationError(w http.ResponseWriter, r *http.Request, err error) {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(sharedapi.ErrorResponse{
ErrorCode: "VALIDATION",
ErrorMessage: err.Error(),
}); err != nil {
sharedlogging.GetLogger(r.Context()).Info("Error validating request: %s", err)
}
}

func internalServerError(w http.ResponseWriter, r *http.Request, err error) {
w.WriteHeader(http.StatusInternalServerError)
if err := json.NewEncoder(w).Encode(sharedapi.ErrorResponse{
ErrorCode: "INTERNAL",
ErrorMessage: err.Error(),
}); err != nil {
trace.SpanFromContext(r.Context()).RecordError(err)
}
}

func createSecret(writer http.ResponseWriter, request *http.Request) {
writer.WriteHeader(http.StatusNotImplemented)
func writeObject[T any](w http.ResponseWriter, r *http.Request, v T) {
if err := json.NewEncoder(w).Encode(sharedapi.BaseResponse[T]{
Data: &v,
}); err != nil {
trace.SpanFromContext(r.Context()).RecordError(err)
}
}

func readClient(writer http.ResponseWriter, request *http.Request) {
writer.WriteHeader(http.StatusNotImplemented)
type client struct {
auth.ClientOptions
ID string `json:"id"`
}

func deleteSecret(db *gorm.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
client := &auth.Client{}
if err := db.Find(client, "id = ?", mux.Vars(r)["clientId"]).Error; err != nil {
switch err {
case gorm.ErrRecordNotFound:
w.WriteHeader(http.StatusNotFound)
default:
internalServerError(w, r, err)
}
return
}

if !client.DeleteSecret(mux.Vars(r)["secretId"]) {
w.WriteHeader(http.StatusNotFound)
return
}

if err := db.Save(client).Error; err != nil {
internalServerError(w, r, err)
return
}
}
}

func patchClient(writer http.ResponseWriter, request *http.Request) {
writer.WriteHeader(http.StatusNotImplemented)
type secretCreate struct {
Name string `json:"name"`
}

type secretCreateResult struct {
ID string `json:"id"`
LastDigits string `json:"lastDigits"`
Name string `json:"name"`
Clear string `json:"clear"`
}

func createSecret(db *gorm.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
client := &auth.Client{}
if err := db.Find(client, "id = ?", mux.Vars(r)["clientId"]).Error; err != nil {
switch err {
case gorm.ErrRecordNotFound:
w.WriteHeader(http.StatusNotFound)
default:
internalServerError(w, r, err)
}
return
}

sc := secretCreate{}
if err := json.NewDecoder(r.Body).Decode(&sc); err != nil {
validationError(w, r, err)
return
}

secret, clear := client.GenerateNewSecret(sc.Name)

if err := db.Save(client).Error; err != nil {
internalServerError(w, r, err)
return
}

w.WriteHeader(http.StatusOK)

writeObject(w, r, secretCreateResult{
ID: secret.ID,
LastDigits: secret.LastDigits,
Name: secret.Name,
Clear: clear,
})
}
}

func updateClient(writer http.ResponseWriter, request *http.Request) {
writer.WriteHeader(http.StatusNotImplemented)
func readClient(db *gorm.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
client := &auth.Client{}
if err := db.Find(client, "id = ?", mux.Vars(r)["clientId"]).Error; err != nil {
switch err {
case gorm.ErrRecordNotFound:
w.WriteHeader(http.StatusNotFound)
default:
internalServerError(w, r, err)
}
return
}
writeObject(w, r, client)
}
}

func listClients(writer http.ResponseWriter, request *http.Request) {
writer.WriteHeader(http.StatusNotImplemented)
func listClients(db *gorm.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
clients := make([]auth.Client, 0)
if err := db.Find(&clients).Error; err != nil {
internalServerError(w, r, err)
return
}
writeObject(w, r, clients)
}
}

func createClient(writer http.ResponseWriter, request *http.Request) {
writer.WriteHeader(http.StatusNotImplemented)
func updateClient(db *gorm.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {

c := auth.Client{}
if err := db.First(&c, "id = ?", mux.Vars(r)["clientId"]).Error; err != nil {
switch err {
case gorm.ErrRecordNotFound:
w.WriteHeader(http.StatusNotFound)
default:
internalServerError(w, r, err)
}
return
}

opts := auth.ClientOptions{}
if err := json.NewDecoder(r.Body).Decode(&opts); err != nil {
validationError(w, r, err)
return
}

c.Update(opts)

if err := db.Save(c).Error; err != nil {
internalServerError(w, r, err)
return
}

w.WriteHeader(http.StatusOK)

writeObject(w, r, client{
ClientOptions: opts,
ID: c.Id,
})
}
}

func updateClientScopes(writer http.ResponseWriter, request *http.Request) {
writer.WriteHeader(http.StatusNotImplemented)
func createClient(db *gorm.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
opts := auth.ClientOptions{}
if err := json.NewDecoder(r.Body).Decode(&opts); err != nil {
validationError(w, r, err)
return
}

grantTypes := []oidc.GrantType{
oidc.GrantTypeCode,
oidc.GrantTypeRefreshToken,
}
if !opts.Public {
grantTypes = append(grantTypes, oidc.GrantTypeClientCredentials)
}

c := auth.NewClient(opts)
if err := db.Create(c).Error; err != nil {
internalServerError(w, r, err)
return
}

w.WriteHeader(http.StatusCreated)
w.Header().Set("Location", "./"+c.Id)

writeObject(w, r, client{
ClientOptions: opts,
ID: c.Id,
})
}
}
Loading

0 comments on commit cc82aaa

Please sign in to comment.