From 71d39a5c74fe9068cc56163e85ec388753cbaf6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20=C5=A0ediv=C3=BD?= Date: Sat, 31 Oct 2020 10:48:24 +0100 Subject: [PATCH] add API JWT auth. --- internal/api/member/handler.go | 13 ++++++++-- internal/api/room/handler.go | 19 ++++++++------- internal/api/router.go | 42 ++++++++++++++++++++++++++++----- internal/http/http.go | 2 +- internal/types/config/server.go | 14 +++++++++++ 5 files changed, 73 insertions(+), 17 deletions(-) diff --git a/internal/api/member/handler.go b/internal/api/member/handler.go index 088212db..427f096b 100644 --- a/internal/api/member/handler.go +++ b/internal/api/member/handler.go @@ -23,10 +23,19 @@ func New( } } -func (h *MemberHandler) Router() *chi.Mux { +func (h *MemberHandler) Router( + usersOnly func(chi.Router, func(chi.Router)), + adminsOnly func(chi.Router, func(chi.Router)), +) *chi.Mux { r := chi.NewRouter() - // TODO + usersOnly(r, func(r chi.Router) { + + }) + + adminsOnly(r, func(r chi.Router) { + + }) return r } diff --git a/internal/api/room/handler.go b/internal/api/room/handler.go index b03e9760..f30af980 100644 --- a/internal/api/room/handler.go +++ b/internal/api/room/handler.go @@ -29,17 +29,20 @@ func New( } } -func (h *RoomHandler) Router() *chi.Mux { +func (h *RoomHandler) Router( + usersOnly func(chi.Router, func(chi.Router)), + adminsOnly func(chi.Router, func(chi.Router)), +) *chi.Mux { r := chi.NewRouter() - - r.Route("/screen", func(r chi.Router) { - r.Get("/", h.ScreenConfiguration) - r.Post("/", h.ScreenConfigurationChange) - - r.Get("/configurations", h.ScreenConfigurationsList) + + usersOnly(r, func(r chi.Router) { + r.Get("/screen", h.ScreenConfiguration) }) - // TODO + adminsOnly(r, func(r chi.Router) { + r.Post("/screen", h.ScreenConfigurationChange) + r.Get("/screen/configurations", h.ScreenConfigurationsList) + }) return r } diff --git a/internal/api/router.go b/internal/api/router.go index c7e0285c..9d04433c 100644 --- a/internal/api/router.go +++ b/internal/api/router.go @@ -2,10 +2,12 @@ package api import ( "github.com/go-chi/chi" + "github.com/go-chi/jwtauth" "demodesk/neko/internal/api/member" "demodesk/neko/internal/api/room" "demodesk/neko/internal/types" + "demodesk/neko/internal/types/config" ) type API struct { @@ -15,13 +17,18 @@ type API struct { websocket types.WebSocketHandler } +var AdminToken *jwtauth.JWTAuth +var UserToken *jwtauth.JWTAuth + func New( sessions types.SessionManager, remote types.RemoteManager, broadcast types.BroadcastManager, websocket types.WebSocketHandler, + conf *config.Server, ) *API { - // Init + AdminToken = jwtauth.New("HS256", []byte(conf.AdminToken), nil) + UserToken = jwtauth.New("HS256", []byte(conf.UserToken), nil) return &API{ sessions: sessions, @@ -31,12 +38,35 @@ func New( } } -func (a *API) Mount(router *chi.Mux) { - // all member routes +func (a *API) Mount(r *chi.Mux) { memberHandler := member.New(a.sessions, a.websocket) - router.Mount("/member", memberHandler.Router()) + r.Mount("/member", memberHandler.Router(UsersOnly, AdminsOnly)) - // get room routes roomHandler := room.New(a.sessions, a.remote, a.broadcast, a.websocket) - router.Mount("/room", roomHandler.Router()) + r.Mount("/room", roomHandler.Router(UsersOnly, AdminsOnly)) +} + +func UsersOnly(r chi.Router, protectedRoutes func(r chi.Router)) { + r.Group(func(r chi.Router) { + // Verify JWT tokens + r.Use(jwtauth.Verifier(UserToken)) + r.Use(jwtauth.Verifier(AdminToken)) + + // Handle valid / invalid tokens. + r.Use(jwtauth.Authenticator) + + protectedRoutes(r) + }) +} + +func AdminsOnly(r chi.Router, protectedRoutes func(r chi.Router)) { + r.Group(func(r chi.Router) { + // Verify JWT token + r.Use(jwtauth.Verifier(AdminToken)) + + // Handle valid / invalid tokens. + r.Use(jwtauth.Authenticator) + + protectedRoutes(r) + }) } diff --git a/internal/http/http.go b/internal/http/http.go index cab997d1..9136d85e 100644 --- a/internal/http/http.go +++ b/internal/http/http.go @@ -39,7 +39,7 @@ func New( router.Use(Logger) // Log API request calls using custom logger function // Mount REST API - apiManager := api.New(sessions, remote, broadcast, webSocketHandler) + apiManager := api.New(sessions, remote, broadcast, webSocketHandler, conf) apiManager.Mount(router) router.Get("/ws", func(w http.ResponseWriter, r *http.Request) { diff --git a/internal/types/config/server.go b/internal/types/config/server.go index fcb4b912..627e3061 100644 --- a/internal/types/config/server.go +++ b/internal/types/config/server.go @@ -10,6 +10,8 @@ type Server struct { Key string Bind string Static string + UserToken string + AdminToken string } func (Server) Init(cmd *cobra.Command) error { @@ -33,6 +35,16 @@ func (Server) Init(cmd *cobra.Command) error { return err } + cmd.PersistentFlags().String("user_token", "user_secret", "JWT token for users") + if err := viper.BindPFlag("user_token", cmd.PersistentFlags().Lookup("user_token")); err != nil { + return err + } + + cmd.PersistentFlags().String("admin_token", "admin_secret", "JWT token for admins") + if err := viper.BindPFlag("admin_token", cmd.PersistentFlags().Lookup("admin_token")); err != nil { + return err + } + return nil } @@ -41,4 +53,6 @@ func (s *Server) Set() { s.Key = viper.GetString("key") s.Bind = viper.GetString("bind") s.Static = viper.GetString("static") + s.UserToken = viper.GetString("user_token") + s.AdminToken = viper.GetString("admin_token") }