forked from mystiq/dex
95757e8779
This is instead of oidc.ClientIdentity. This makes it easier to add new fields custom to dex to the client.
149 lines
3.7 KiB
Go
149 lines
3.7 KiB
Go
package server
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"path"
|
|
|
|
"github.com/coreos/pkg/health"
|
|
"github.com/julienschmidt/httprouter"
|
|
|
|
"github.com/coreos/dex/admin"
|
|
"github.com/coreos/dex/pkg/log"
|
|
"github.com/coreos/dex/schema/adminschema"
|
|
"github.com/coreos/go-oidc/key"
|
|
)
|
|
|
|
const (
|
|
AdminAPIVersion = "v1"
|
|
AdminAPISecretLength = 128
|
|
)
|
|
|
|
var (
|
|
AdminGetEndpoint = addBasePath("/admin/:id")
|
|
AdminCreateEndpoint = addBasePath("/admin")
|
|
AdminGetStateEndpoint = addBasePath("/state")
|
|
AdminCreateClientEndpoint = addBasePath("/client")
|
|
)
|
|
|
|
// AdminServer serves the admin API.
|
|
type AdminServer struct {
|
|
adminAPI *admin.AdminAPI
|
|
checker health.Checker
|
|
secret string
|
|
}
|
|
|
|
func NewAdminServer(adminAPI *admin.AdminAPI, rotator *key.PrivateKeyRotator, secret string) *AdminServer {
|
|
return &AdminServer{
|
|
adminAPI: adminAPI,
|
|
checker: health.Checker{
|
|
Checks: []health.Checkable{
|
|
rotator,
|
|
},
|
|
},
|
|
secret: secret,
|
|
}
|
|
}
|
|
|
|
func (s *AdminServer) HTTPHandler() http.Handler {
|
|
r := httprouter.New()
|
|
r.GET(AdminGetEndpoint, s.getAdmin)
|
|
r.POST(AdminCreateEndpoint, s.createAdmin)
|
|
r.GET(AdminGetStateEndpoint, s.getState)
|
|
r.POST(AdminCreateClientEndpoint, s.createClient)
|
|
r.Handler("GET", httpPathHealth, s.checker)
|
|
r.HandlerFunc("GET", httpPathDebugVars, health.ExpvarHandler)
|
|
|
|
return authorizer(r, s.secret, httpPathHealth, httpPathDebugVars)
|
|
}
|
|
|
|
func authorizer(h http.Handler, secret string, public ...string) http.Handler {
|
|
publicSet := map[string]struct{}{}
|
|
for _, p := range public {
|
|
publicSet[p] = struct{}{}
|
|
}
|
|
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
_, isPublicPath := publicSet[r.URL.Path]
|
|
|
|
if !isPublicPath && r.Header.Get("Authorization") != secret {
|
|
writeAPIError(w, http.StatusUnauthorized, newAPIError(errorAccessDenied, ""))
|
|
return
|
|
}
|
|
h.ServeHTTP(w, r)
|
|
})
|
|
}
|
|
|
|
func (s *AdminServer) getAdmin(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
|
id := ps.ByName("id")
|
|
|
|
admn, err := s.adminAPI.GetAdmin(id)
|
|
if err != nil {
|
|
s.writeError(w, err)
|
|
return
|
|
}
|
|
|
|
writeResponseWithBody(w, http.StatusOK, admn)
|
|
}
|
|
|
|
func (s *AdminServer) createAdmin(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
|
admn := adminschema.Admin{}
|
|
err := json.NewDecoder(r.Body).Decode(&admn)
|
|
if err != nil {
|
|
writeInvalidRequest(w, "cannot parse JSON body")
|
|
return
|
|
}
|
|
|
|
id, err := s.adminAPI.CreateAdmin(admn)
|
|
if err != nil {
|
|
s.writeError(w, err)
|
|
return
|
|
}
|
|
|
|
admn.Id = id
|
|
w.Header().Set("Location", AdminCreateEndpoint+"/"+id)
|
|
writeResponseWithBody(w, http.StatusOK, admn)
|
|
}
|
|
|
|
func (s *AdminServer) getState(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
|
state, err := s.adminAPI.GetState()
|
|
if err != nil {
|
|
s.writeError(w, err)
|
|
return
|
|
}
|
|
|
|
writeResponseWithBody(w, http.StatusOK, state)
|
|
}
|
|
|
|
func (s *AdminServer) createClient(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
|
var req = adminschema.ClientCreateRequest{}
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
writeInvalidRequest(w, "cannot parse JSON body")
|
|
return
|
|
}
|
|
|
|
resp, err := s.adminAPI.CreateClient(req)
|
|
if err != nil {
|
|
s.writeError(w, err)
|
|
return
|
|
}
|
|
writeResponseWithBody(w, http.StatusOK, &resp)
|
|
}
|
|
|
|
func (s *AdminServer) writeError(w http.ResponseWriter, err error) {
|
|
log.Errorf("Error calling admin API: %v: ", err)
|
|
if adminErr, ok := err.(admin.Error); ok {
|
|
writeAPIError(w, adminErr.Code, newAPIError(adminErr.Type, adminErr.Desc))
|
|
return
|
|
}
|
|
|
|
writeAPIError(w, http.StatusInternalServerError, newAPIError(errorServerError, err.Error()))
|
|
}
|
|
|
|
func writeInvalidRequest(w http.ResponseWriter, msg string) {
|
|
writeAPIError(w, http.StatusBadRequest, newAPIError(errorInvalidRequest, msg))
|
|
}
|
|
|
|
func addBasePath(s string) string {
|
|
return path.Join(httpPathAPI, APIVersion, s)
|
|
}
|