Skip to content

Commit

Permalink
Add user management functionality; update API handlers to include pri…
Browse files Browse the repository at this point in the history
…ncipal parameter and create database schema for users and tokens
  • Loading branch information
inhogog2 committed Jan 23, 2025
1 parent 3a4368d commit 5f2129d
Show file tree
Hide file tree
Showing 32 changed files with 1,416 additions and 114 deletions.
62 changes: 62 additions & 0 deletions api/models/user.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

29 changes: 29 additions & 0 deletions api/restapi/configure_loxilb_rest_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ import (

"github.com/loxilb-io/loxilb/api/restapi/handler"
"github.com/loxilb-io/loxilb/api/restapi/operations"
"github.com/loxilb-io/loxilb/api/restapi/operations/auth"
"github.com/loxilb-io/loxilb/api/restapi/operations/users"
)

//go:generate swagger generate server --target ../../api --name LoxilbRestAPI --spec ../swagger.yml --principal interface{}
Expand Down Expand Up @@ -57,6 +59,10 @@ func configureAPI(api *operations.LoxilbRestAPIAPI) http.Handler {
api.JSONConsumer = runtime.JSONConsumer()

api.JSONProducer = runtime.JSONProducer()
// Applies when the "Authorization" header is set
api.BearerAuthAuth = handler.BearerAuthAuth
// Set your custom authorizer if needed. Default one is security.Authorized()
api.APIAuthorizer = handler.Authorized()

// Load balancer add and delete and get
api.PostConfigLoadbalancerHandler = operations.PostConfigLoadbalancerHandlerFunc(handler.ConfigPostLoadbalancer)
Expand Down Expand Up @@ -187,6 +193,19 @@ func configureAPI(api *operations.LoxilbRestAPIAPI) http.Handler {
// Version
api.GetVersionHandler = operations.GetVersionHandlerFunc(handler.ConfigGetVersion)

// It works only if the UserServiceEnable option is enabled.
if opts.Opts.UserServiceEnable {
// login logout api
api.AuthPostAuthLoginHandler = auth.PostAuthLoginHandlerFunc(handler.AuthPostLogin)
api.AuthPostAuthLogoutHandler = auth.PostAuthLogoutHandlerFunc(handler.AuthPostLogout)

// Users API
api.UsersGetAuthUsersHandler = users.GetAuthUsersHandlerFunc(handler.UsersGetUsers)
api.UsersPostAuthUsersHandler = users.PostAuthUsersHandlerFunc(handler.UsersPostUsers)
api.UsersDeleteAuthUsersIDHandler = users.DeleteAuthUsersIDHandlerFunc(handler.UsersDeleteUsers)
api.UsersPutAuthUsersIDHandler = users.PutAuthUsersIDHandlerFunc(handler.UsersPutUsers)
}

api.PreServerShutdown = func() {}
api.ServerShutdown = func() {}

Expand All @@ -208,6 +227,16 @@ func configureServer(s *http.Server, scheme, addr string) {
// The middleware configuration is for the handler executors. These do not apply to the swagger.json document.
// The middleware executes after routing but before authentication, binding and validation.
func setupMiddlewares(handler http.Handler) http.Handler {
// User service is disabled, so we need to set a valid token for the Authorization header.
if !opts.Opts.UserServiceEnable {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Set a any token for the Authorization header.
if r.Header.Get("Authorization") == "" {
r.Header.Set("Authorization", "valid")
}
handler.ServeHTTP(w, r)
})
}
return handler
}

Expand Down
106 changes: 106 additions & 0 deletions api/restapi/handler/auth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*
* Copyright (c) 2022 NetLOX Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package handler

import (
"errors"
"net/http"
"strings"

"github.com/go-openapi/runtime"
"github.com/go-openapi/runtime/middleware"

"github.com/loxilb-io/loxilb/api/models"
"github.com/loxilb-io/loxilb/api/restapi/operations/auth"
cmn "github.com/loxilb-io/loxilb/common"
opts "github.com/loxilb-io/loxilb/options"
)

// BearerAuthAuth parses and validates a JWT token string.
// It returns the claims contained in the token if it is valid, or an error if the token is invalid or parsing fails.
// But if the UserServiceEnable option is disabled, it will return true.
//
// Parameters:
// - tokenString: the JWT token string to be validated.
//
// Returns:
// - bool: the claims contained in the token if it is valid.
// - error: an error if the token is invalid or parsing fails.
func BearerAuthAuth(tokenString string) (interface{}, error) {
if opts.Opts.UserServiceEnable {
return ApiHooks.NetUserValidate(tokenString)
} else {
return true, nil
}
}

// AuthPostLogin function
// This function is used to authenticate the user and generate a token
func AuthPostLogin(params auth.PostAuthLoginParams) middleware.Responder {
var response models.LoginResponse
var user cmn.User
user.Username = params.User.Username
user.Password = params.User.Password
token, valid, err := ApiHooks.NetUserLogin(&user)
if err != nil {
return &ResultResponse{Result: err.Error()}
}
if valid {
response.Token = token
}
return auth.NewPostAuthLoginOK().WithPayload(&response)
}

// AuthPostLogout function
// This function is used to logout the user
func AuthPostLogout(params auth.PostAuthLogoutParams, principal interface{}) middleware.Responder {
token := params.HTTPRequest.Header.Get("Authorization")
err := ApiHooks.NetUserLogout(token)
if err != nil {
return &ResultResponse{Result: err.Error()}
}
return auth.NewPostAuthLogoutOK()
}

// Authorized function to handle authorization logic
// requests are authorized based on the role of the user
func Authorized() runtime.Authorizer {
if opts.Opts.UserServiceEnable {
return runtime.AuthorizerFunc(func(param *http.Request, principal interface{}) error {
permitInfo := principal.(string)
// Viewer user can only GET requests
UserNameAndRole := strings.Split(permitInfo, "|")
if len(UserNameAndRole) != 2 {
return errors.New("Invalid user info. Please contact the administrator")
}
UserRole := UserNameAndRole[1]
// Viewer user can only GET requests except for logout
if strings.Contains(UserRole, "viewer") && param.Method == "POST" && param.URL.Path == "/netlox/v1/auth/logout" {
return nil
} else if strings.Contains(UserRole, "viewer") && param.Method != "GET" {
return errors.New("Permission denied")
}
return nil
})
} else {
return runtime.AuthorizerFunc(func(_ *http.Request, _ interface{}) error {

return nil
})

}

}
10 changes: 5 additions & 5 deletions api/restapi/handler/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import (
tk "github.com/loxilb-io/loxilib"
)

func ConfigGetCIState(params operations.GetConfigCistateAllParams) middleware.Responder {
func ConfigGetCIState(params operations.GetConfigCistateAllParams, principal interface{}) middleware.Responder {
var result []*models.CIStatusGetEntry
result = make([]*models.CIStatusGetEntry, 0)
tk.LogIt(tk.LogTrace, "api: Status %s API called. url : %s\n", params.HTTPRequest.Method, params.HTTPRequest.URL)
Expand All @@ -45,7 +45,7 @@ func ConfigGetCIState(params operations.GetConfigCistateAllParams) middleware.Re
return operations.NewGetConfigCistateAllOK().WithPayload(&operations.GetConfigCistateAllOKBody{Attr: result})
}

func ConfigPostCIState(params operations.PostConfigCistateParams) middleware.Responder {
func ConfigPostCIState(params operations.PostConfigCistateParams, principal interface{}) middleware.Responder {
tk.LogIt(tk.LogTrace, "api: HA %s API called. url : %s\n", params.HTTPRequest.Method, params.HTTPRequest.URL)

var hasMod cmn.HASMod
Expand All @@ -64,7 +64,7 @@ func ConfigPostCIState(params operations.PostConfigCistateParams) middleware.Res
return &ResultResponse{Result: "Success"}
}

func ConfigGetBFDSession(params operations.GetConfigBfdAllParams) middleware.Responder {
func ConfigGetBFDSession(params operations.GetConfigBfdAllParams, principal interface{}) middleware.Responder {
var result []*models.BfdGetEntry
result = make([]*models.BfdGetEntry, 0)
tk.LogIt(tk.LogTrace, "api: Status %s API called. url : %s\n", params.HTTPRequest.Method, params.HTTPRequest.URL)
Expand All @@ -89,7 +89,7 @@ func ConfigGetBFDSession(params operations.GetConfigBfdAllParams) middleware.Res
return operations.NewGetConfigBfdAllOK().WithPayload(&operations.GetConfigBfdAllOKBody{Attr: result})
}

func ConfigPostBFDSession(params operations.PostConfigBfdParams) middleware.Responder {
func ConfigPostBFDSession(params operations.PostConfigBfdParams, principal interface{}) middleware.Responder {
tk.LogIt(tk.LogTrace, "api: HA %s API called. url : %s\n", params.HTTPRequest.Method, params.HTTPRequest.URL)

var bfdMod cmn.BFDMod
Expand All @@ -111,7 +111,7 @@ func ConfigPostBFDSession(params operations.PostConfigBfdParams) middleware.Resp
return &ResultResponse{Result: "Success"}
}

func ConfigDeleteBFDSession(params operations.DeleteConfigBfdRemoteIPRemoteIPParams) middleware.Responder {
func ConfigDeleteBFDSession(params operations.DeleteConfigBfdRemoteIPRemoteIPParams, principal interface{}) middleware.Responder {
tk.LogIt(tk.LogTrace, "api: HA %s API called. url : %s\n", params.HTTPRequest.Method, params.HTTPRequest.URL)

var bfdMod cmn.BFDMod
Expand Down
2 changes: 1 addition & 1 deletion api/restapi/handler/conntrack.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import (
tk "github.com/loxilb-io/loxilib"
)

func ConfigGetConntrack(params operations.GetConfigConntrackAllParams) middleware.Responder {
func ConfigGetConntrack(params operations.GetConfigConntrackAllParams, principal interface{}) middleware.Responder {
tk.LogIt(tk.LogTrace, "api: Conntrack %s API called. url : %s\n", params.HTTPRequest.Method, params.HTTPRequest.URL)
// Get Conntrack informations
res, err := ApiHooks.NetCtInfoGet()
Expand Down
6 changes: 3 additions & 3 deletions api/restapi/handler/endpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import (
tk "github.com/loxilb-io/loxilib"
)

func ConfigGetEndPoint(params operations.GetConfigEndpointAllParams) middleware.Responder {
func ConfigGetEndPoint(params operations.GetConfigEndpointAllParams, principal interface{}) middleware.Responder {
// Get endpoint rules
tk.LogIt(tk.LogTrace, "api: EndPoint %s API called. url : %s\n", params.HTTPRequest.Method, params.HTTPRequest.URL)

Expand Down Expand Up @@ -57,7 +57,7 @@ func ConfigGetEndPoint(params operations.GetConfigEndpointAllParams) middleware.
return operations.NewGetConfigEndpointAllOK().WithPayload(&operations.GetConfigEndpointAllOKBody{Attr: result})
}

func ConfigPostEndPoint(params operations.PostConfigEndpointParams) middleware.Responder {
func ConfigPostEndPoint(params operations.PostConfigEndpointParams, principal interface{}) middleware.Responder {
tk.LogIt(tk.LogTrace, "api: EndPoint %s API called. url : %s\n", params.HTTPRequest.Method, params.HTTPRequest.URL)

EP := cmn.EndPointMod{}
Expand All @@ -77,7 +77,7 @@ func ConfigPostEndPoint(params operations.PostConfigEndpointParams) middleware.R
return &ResultResponse{Result: "Success"}
}

func ConfigDeleteEndPoint(params operations.DeleteConfigEndpointEpipaddressIPAddressParams) middleware.Responder {
func ConfigDeleteEndPoint(params operations.DeleteConfigEndpointEpipaddressIPAddressParams, principal interface{}) middleware.Responder {
tk.LogIt(tk.LogTrace, "api: EndPoint %s API called. url : %s\n", params.HTTPRequest.Method, params.HTTPRequest.URL)

EP := cmn.EndPointMod{}
Expand Down
6 changes: 3 additions & 3 deletions api/restapi/handler/fdb.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import (
tk "github.com/loxilb-io/loxilib"
)

func ConfigPostFDB(params operations.PostConfigFdbParams) middleware.Responder {
func ConfigPostFDB(params operations.PostConfigFdbParams, principal interface{}) middleware.Responder {
tk.LogIt(tk.LogTrace, "api: FDB %s API called. url : %s\n", params.HTTPRequest.Method, params.HTTPRequest.URL)
ret := loxinlp.AddFDBNoHook(params.Attr.MacAddress, params.Attr.Dev)
if ret != 0 {
Expand All @@ -32,7 +32,7 @@ func ConfigPostFDB(params operations.PostConfigFdbParams) middleware.Responder {
return &ResultResponse{Result: "Success"}
}

func ConfigDeleteFDB(params operations.DeleteConfigFdbMacAddressDevIfNameParams) middleware.Responder {
func ConfigDeleteFDB(params operations.DeleteConfigFdbMacAddressDevIfNameParams, principal interface{}) middleware.Responder {
tk.LogIt(tk.LogTrace, "api: FDB %s API called. url : %s\n", params.HTTPRequest.Method, params.HTTPRequest.URL)
ret := loxinlp.DelFDBNoHook(params.MacAddress, params.IfName)
if ret != 0 {
Expand All @@ -41,7 +41,7 @@ func ConfigDeleteFDB(params operations.DeleteConfigFdbMacAddressDevIfNameParams)
return &ResultResponse{Result: "Success"}
}

func ConfigGetFDB(params operations.GetConfigFdbAllParams) middleware.Responder {
func ConfigGetFDB(params operations.GetConfigFdbAllParams, principal interface{}) middleware.Responder {
tk.LogIt(tk.LogTrace, "api: FDB %s API called. url : %s\n", params.HTTPRequest.Method, params.HTTPRequest.URL)
fdbs, _ := loxinlp.GetFDBNoHook()
var result []*models.FDBEntry
Expand Down
6 changes: 3 additions & 3 deletions api/restapi/handler/firewall.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import (
tk "github.com/loxilb-io/loxilib"
)

func ConfigPostFW(params operations.PostConfigFirewallParams) middleware.Responder {
func ConfigPostFW(params operations.PostConfigFirewallParams, principal interface{}) middleware.Responder {
tk.LogIt(tk.LogTrace, "api: Firewall %s API called by IP: %s. url : %s\n", params.HTTPRequest.Method, params.HTTPRequest.RemoteAddr, params.HTTPRequest.URL)
Opts := cmn.FwOptArg{}
Rules := cmn.FwRuleArg{}
Expand Down Expand Up @@ -80,7 +80,7 @@ func ConfigPostFW(params operations.PostConfigFirewallParams) middleware.Respond
return &ResultResponse{Result: "Success"}
}

func ConfigDeleteFW(params operations.DeleteConfigFirewallParams) middleware.Responder {
func ConfigDeleteFW(params operations.DeleteConfigFirewallParams, principal interface{}) middleware.Responder {
tk.LogIt(tk.LogTrace, "api: Firewall %s API called by IP: %s. url : %s\n", params.HTTPRequest.Method, params.HTTPRequest.RemoteAddr, params.HTTPRequest.URL)

Rules := cmn.FwRuleArg{}
Expand Down Expand Up @@ -144,7 +144,7 @@ func ConfigDeleteFW(params operations.DeleteConfigFirewallParams) middleware.Res
return &ResultResponse{Result: "Success"}
}

func ConfigGetFW(params operations.GetConfigFirewallAllParams) middleware.Responder {
func ConfigGetFW(params operations.GetConfigFirewallAllParams, principal interface{}) middleware.Responder {
tk.LogIt(tk.LogTrace, "api: Firewall %s API called by IP: %s. url : %s\n", params.HTTPRequest.Method, params.HTTPRequest.RemoteAddr, params.HTTPRequest.URL)
res, _ := ApiHooks.NetFwRuleGet()
var result []*models.FirewallEntry
Expand Down
Loading

0 comments on commit 5f2129d

Please sign in to comment.