server: add refresh token revocation API to server
This commit is contained in:
parent
64380734e6
commit
553e7d0167
3 changed files with 54 additions and 10 deletions
|
@ -160,6 +160,7 @@ func (cfg *SingleServerConfig) Configure(srv *Server) error {
|
|||
srv.SessionManager = sm
|
||||
srv.RefreshTokenRepo = refTokRepo
|
||||
srv.HealthChecks = append(srv.HealthChecks, db.NewHealthChecker(dbMap))
|
||||
srv.dbMap = dbMap
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -253,6 +254,7 @@ func (cfg *MultiServerConfig) Configure(srv *Server) error {
|
|||
srv.SessionManager = sm
|
||||
srv.RefreshTokenRepo = refreshTokenRepo
|
||||
srv.HealthChecks = append(srv.HealthChecks, db.NewHealthChecker(dbc))
|
||||
srv.dbMap = dbc
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ import (
|
|||
"github.com/coreos/go-oidc/oauth2"
|
||||
"github.com/coreos/go-oidc/oidc"
|
||||
"github.com/coreos/pkg/health"
|
||||
"github.com/go-gorp/gorp"
|
||||
"github.com/jonboulle/clockwork"
|
||||
|
||||
"github.com/coreos/dex/client"
|
||||
|
@ -77,6 +78,7 @@ type Server struct {
|
|||
EnableRegistration bool
|
||||
EnableClientRegistration bool
|
||||
|
||||
dbMap *gorp.DbMap
|
||||
localConnectorID string
|
||||
}
|
||||
|
||||
|
@ -270,12 +272,10 @@ func (s *Server) HTTPHandler() http.Handler {
|
|||
clientPath, clientHandler := registerClientResource(apiBasePath, s.ClientIdentityRepo)
|
||||
mux.Handle(path.Join(apiBasePath, clientPath), s.NewClientTokenAuthHandler(clientHandler))
|
||||
|
||||
usersAPI := usersapi.NewUsersAPI(s.UserManager, s.ClientIdentityRepo, s.UserEmailer, s.localConnectorID)
|
||||
usersAPI := usersapi.NewUsersAPI(s.dbMap, s.UserManager, s.UserEmailer, s.localConnectorID)
|
||||
handler := NewUserMgmtServer(usersAPI, s.JWTVerifierFactory(), s.UserManager, s.ClientIdentityRepo).HTTPHandler()
|
||||
path := path.Join(apiBasePath, UsersSubTree)
|
||||
|
||||
mux.Handle(path, handler)
|
||||
mux.Handle(path+"/", handler)
|
||||
mux.Handle(apiBasePath+"/", handler)
|
||||
|
||||
return http.Handler(mux)
|
||||
}
|
||||
|
|
|
@ -30,6 +30,9 @@ var (
|
|||
UsersGetEndpoint = addBasePath(UsersSubTree + "/:id")
|
||||
UsersDisableEndpoint = addBasePath(UsersSubTree + "/:id/disable")
|
||||
UsersResendInvitationEndpoint = addBasePath(UsersSubTree + "/:id/resend-invitation")
|
||||
AccountSubTree = "/account"
|
||||
AccountListRefreshTokens = addBasePath(AccountSubTree + "/:userid/refresh")
|
||||
AccountRevokeRefreshToken = addBasePath(AccountSubTree + "/:userid/refresh/:clientid")
|
||||
)
|
||||
|
||||
type UserMgmtServer struct {
|
||||
|
@ -52,26 +55,48 @@ func (s *UserMgmtServer) HTTPHandler() http.Handler {
|
|||
r := httprouter.New()
|
||||
r.RedirectTrailingSlash = false
|
||||
r.RedirectFixedPath = false
|
||||
r.GET(UsersListEndpoint, s.authAPIHandle(s.listUsers))
|
||||
r.POST(UsersCreateEndpoint, s.authAPIHandle(s.createUser))
|
||||
r.POST(UsersDisableEndpoint, s.authAPIHandle(s.disableUser))
|
||||
r.GET(UsersGetEndpoint, s.authAPIHandle(s.getUser))
|
||||
r.POST(UsersResendInvitationEndpoint, s.authAPIHandle(s.resendInvitationEmail))
|
||||
|
||||
r.GET(UsersListEndpoint, s.authAdminUser(s.listUsers))
|
||||
r.POST(UsersCreateEndpoint, s.authAdminUser(s.createUser))
|
||||
r.POST(UsersDisableEndpoint, s.authAdminUser(s.disableUser))
|
||||
r.GET(UsersGetEndpoint, s.authAdminUser(s.getUser))
|
||||
r.POST(UsersResendInvitationEndpoint, s.authAdminUser(s.resendInvitationEmail))
|
||||
|
||||
r.GET(AccountListRefreshTokens, s.authAccount(s.listClientsWithRefreshTokens))
|
||||
r.DELETE(AccountRevokeRefreshToken, s.authAccount(s.revokeRefreshTokensForClient))
|
||||
return r
|
||||
}
|
||||
|
||||
func (s *UserMgmtServer) authAdminUser(handle authedHandle) httprouter.Handle {
|
||||
return s.authAPIHandle(handle, true)
|
||||
}
|
||||
|
||||
func (s *UserMgmtServer) authAccount(handle authedHandle) httprouter.Handle {
|
||||
return s.authAPIHandle(handle, false)
|
||||
}
|
||||
|
||||
// authedHandle is an HTTP handle which requires requests to be authenticated as an admin user.
|
||||
type authedHandle func(w http.ResponseWriter, r *http.Request, ps httprouter.Params, creds api.Creds)
|
||||
|
||||
// authAPIHandle is a middleware function with authenticates an HTTP request before passing
|
||||
// it along to the authedHandle.
|
||||
func (s *UserMgmtServer) authAPIHandle(handle authedHandle) httprouter.Handle {
|
||||
//
|
||||
// The authorization checks for an ID token bearer token in the request header, requiring the
|
||||
// audience (aud claim) be a client ID of an admin client.
|
||||
//
|
||||
// If requiresAdmin is true, the subject identifier (sub claim) of the ID token provided must be
|
||||
// that of an admin user.
|
||||
func (s *UserMgmtServer) authAPIHandle(handle authedHandle, requiresAdmin bool) httprouter.Handle {
|
||||
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||
creds, err := s.getCreds(r)
|
||||
if err != nil {
|
||||
s.writeError(w, err)
|
||||
return
|
||||
}
|
||||
if creds.User.Disabled || (requiresAdmin && !creds.User.Admin) {
|
||||
s.writeError(w, api.ErrorUnauthorized)
|
||||
return
|
||||
}
|
||||
handle(w, r, ps, creds)
|
||||
}
|
||||
}
|
||||
|
@ -191,6 +216,23 @@ func (s *UserMgmtServer) resendInvitationEmail(w http.ResponseWriter, r *http.Re
|
|||
writeResponseWithBody(w, http.StatusOK, resendEmailInvitationResponse)
|
||||
}
|
||||
|
||||
func (s *UserMgmtServer) listClientsWithRefreshTokens(w http.ResponseWriter, r *http.Request, ps httprouter.Params, creds api.Creds) {
|
||||
clients, err := s.api.ListClientsWithRefreshTokens(creds, ps.ByName("userid"))
|
||||
if err != nil {
|
||||
s.writeError(w, err)
|
||||
return
|
||||
}
|
||||
writeResponseWithBody(w, http.StatusOK, schema.RefreshClientList{Clients: clients})
|
||||
}
|
||||
|
||||
func (s *UserMgmtServer) revokeRefreshTokensForClient(w http.ResponseWriter, r *http.Request, ps httprouter.Params, creds api.Creds) {
|
||||
if err := s.api.RevokeRefreshTokensForClient(creds, ps.ByName("userid"), ps.ByName("clientid")); err != nil {
|
||||
s.writeError(w, err)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusOK) // NOTE (ericchiang): http.StatusNoContent or return an empty JSON object?
|
||||
}
|
||||
|
||||
func (s *UserMgmtServer) writeError(w http.ResponseWriter, err error) {
|
||||
log.Errorf("Error calling user management API: %v: ", err)
|
||||
if apiErr, ok := err.(api.Error); ok {
|
||||
|
|
Reference in a new issue