Browse Source

consolidated security check function

0xdcarns 3 years ago
parent
commit
95528f510a

+ 8 - 62
controllers/dnsHttpController.go

@@ -3,7 +3,6 @@ package controller
 import (
 	"encoding/json"
 	"net/http"
-	"strings"
 
 	"github.com/go-playground/validator/v10"
 	"github.com/gorilla/mux"
@@ -15,14 +14,14 @@ import (
 
 func dnsHandlers(r *mux.Router) {
 
-	r.HandleFunc("/api/dns", securityCheckDNS(true, true, http.HandlerFunc(getAllDNS))).Methods("GET")
-	r.HandleFunc("/api/dns/adm/{network}/nodes", securityCheckDNS(false, true, http.HandlerFunc(getNodeDNS))).Methods("GET")
-	r.HandleFunc("/api/dns/adm/{network}/custom", securityCheckDNS(false, true, http.HandlerFunc(getCustomDNS))).Methods("GET")
-	r.HandleFunc("/api/dns/adm/{network}", securityCheckDNS(false, true, http.HandlerFunc(getDNS))).Methods("GET")
-	r.HandleFunc("/api/dns/{network}", securityCheckDNS(false, false, http.HandlerFunc(createDNS))).Methods("POST")
-	r.HandleFunc("/api/dns/adm/pushdns", securityCheckDNS(false, false, http.HandlerFunc(pushDNS))).Methods("POST")
-	r.HandleFunc("/api/dns/{network}/{domain}", securityCheckDNS(false, false, http.HandlerFunc(deleteDNS))).Methods("DELETE")
-	r.HandleFunc("/api/dns/{network}/{domain}", securityCheckDNS(false, false, http.HandlerFunc(updateDNS))).Methods("PUT")
+	r.HandleFunc("/api/dns", securityCheck(true, http.HandlerFunc(getAllDNS))).Methods("GET")
+	r.HandleFunc("/api/dns/adm/{network}/nodes", securityCheck(false, http.HandlerFunc(getNodeDNS))).Methods("GET")
+	r.HandleFunc("/api/dns/adm/{network}/custom", securityCheck(false, http.HandlerFunc(getCustomDNS))).Methods("GET")
+	r.HandleFunc("/api/dns/adm/{network}", securityCheck(false, http.HandlerFunc(getDNS))).Methods("GET")
+	r.HandleFunc("/api/dns/{network}", securityCheck(false, http.HandlerFunc(createDNS))).Methods("POST")
+	r.HandleFunc("/api/dns/adm/pushdns", securityCheck(false, http.HandlerFunc(pushDNS))).Methods("POST")
+	r.HandleFunc("/api/dns/{network}/{domain}", securityCheck(false, http.HandlerFunc(deleteDNS))).Methods("DELETE")
+	r.HandleFunc("/api/dns/{network}/{domain}", securityCheck(false, http.HandlerFunc(updateDNS))).Methods("PUT")
 }
 
 //Gets all nodes associated with network, including pending nodes
@@ -386,20 +385,6 @@ func ValidateDNSUpdate(change models.DNSEntry, entry models.DNSEntry) error {
 		return err == nil
 	})
 
-	//	_ = v.RegisterValidation("name_valid", func(fl validator.FieldLevel) bool {
-	//		isvalid := functions.NameInDNSCharSet(entry.Name)
-	//		notEmptyCheck := entry.Name != ""
-	//		return isvalid && notEmptyCheck
-	//	})
-	//
-	//	_ = v.RegisterValidation("address_valid", func(fl validator.FieldLevel) bool {
-	//		isValid := true
-	//		if entry.Address != "" {
-	//			isValid = functions.IsIpNet(entry.Address)
-	//		}
-	//		return isValid
-	//	})
-
 	err := v.Struct(change)
 
 	if err != nil {
@@ -409,42 +394,3 @@ func ValidateDNSUpdate(change models.DNSEntry, entry models.DNSEntry) error {
 	}
 	return err
 }
-
-//Security check DNS is middleware for every DNS function and just checks to make sure that its the master or dns token calling
-//Only admin should have access to all these network-level actions
-//DNS token should have access to only read functions
-func securityCheckDNS(reqAdmin bool, allowDNSToken bool, next http.Handler) http.HandlerFunc {
-	return func(w http.ResponseWriter, r *http.Request) {
-		var errorResponse = models.ErrorResponse{
-			Code: http.StatusUnauthorized, Message: "W1R3: It's not you it's me.",
-		}
-
-		var params = mux.Vars(r)
-		bearerToken := r.Header.Get("Authorization")
-		if allowDNSToken && authenticateDNSToken(bearerToken) {
-			r.Header.Set("user", "nameserver")
-			networks, _ := json.Marshal([]string{ALL_NETWORK_ACCESS})
-			r.Header.Set("networks", string(networks))
-			next.ServeHTTP(w, r)
-		} else {
-			err, networks, username := SecurityCheck(reqAdmin, params["networkname"], bearerToken)
-			if err != nil {
-				if strings.Contains(err.Error(), "does not exist") {
-					errorResponse.Code = http.StatusNotFound
-				}
-				errorResponse.Message = err.Error()
-				returnErrorResponse(w, r, errorResponse)
-				return
-			}
-			networksJson, err := json.Marshal(&networks)
-			if err != nil {
-				errorResponse.Message = err.Error()
-				returnErrorResponse(w, r, errorResponse)
-				return
-			}
-			r.Header.Set("user", username)
-			r.Header.Set("networks", string(networksJson))
-			next.ServeHTTP(w, r)
-		}
-	}
-}

+ 0 - 95
controllers/networkHttpController.go

@@ -36,101 +36,6 @@ func networkHandlers(r *mux.Router) {
 	r.HandleFunc("/api/networks/{networkname}/keys/{name}", securityCheck(false, http.HandlerFunc(deleteAccessKey))).Methods("DELETE")
 }
 
-//Security check is middleware for every function and just checks to make sure that its the master calling
-//Only admin should have access to all these network-level actions
-//or maybe some Users once implemented
-func securityCheck(reqAdmin bool, next http.Handler) http.HandlerFunc {
-	return func(w http.ResponseWriter, r *http.Request) {
-		var errorResponse = models.ErrorResponse{
-			Code: http.StatusUnauthorized, Message: "W1R3: It's not you it's me.",
-		}
-		if strings.Contains(r.RequestURI, "/dns") && r.Method == "GET" {
-
-		}
-
-		var params = mux.Vars(r)
-		bearerToken := r.Header.Get("Authorization")
-		err, networks, username := SecurityCheck(reqAdmin, params["networkname"], bearerToken)
-		if err != nil {
-			if strings.Contains(err.Error(), "does not exist") {
-				errorResponse.Code = http.StatusNotFound
-			}
-			errorResponse.Message = err.Error()
-			returnErrorResponse(w, r, errorResponse)
-			return
-		}
-		networksJson, err := json.Marshal(&networks)
-		if err != nil {
-			errorResponse.Message = err.Error()
-			returnErrorResponse(w, r, errorResponse)
-			return
-		}
-		r.Header.Set("user", username)
-		r.Header.Set("networks", string(networksJson))
-		next.ServeHTTP(w, r)
-	}
-}
-
-// SecurityCheck - checks token stuff
-func SecurityCheck(reqAdmin bool, netname string, token string) (error, []string, string) {
-
-	var hasBearer = true
-	var tokenSplit = strings.Split(token, " ")
-	var authToken = ""
-
-	if len(tokenSplit) < 2 {
-		hasBearer = false
-	} else {
-		authToken = tokenSplit[1]
-	}
-	userNetworks := []string{}
-	//all endpoints here require master so not as complicated
-	isMasterAuthenticated := authenticateMaster(authToken)
-	username := ""
-	if !hasBearer || !isMasterAuthenticated {
-		userName, networks, isadmin, err := logic.VerifyUserToken(authToken)
-		username = userName
-		if err != nil {
-			return errors.New("error verifying user token"), nil, username
-		}
-		if !isadmin && reqAdmin {
-			return errors.New("you are unauthorized to access this endpoint"), nil, username
-		}
-		userNetworks = networks
-		if isadmin {
-			userNetworks = []string{ALL_NETWORK_ACCESS}
-		} else {
-			networkexists, err := functions.NetworkExists(netname)
-			if err != nil && !database.IsEmptyRecord(err) {
-				return err, nil, ""
-			}
-			if netname != "" && !networkexists {
-				return errors.New("this network does not exist"), nil, ""
-			}
-		}
-	} else if isMasterAuthenticated {
-		userNetworks = []string{ALL_NETWORK_ACCESS}
-	}
-	if len(userNetworks) == 0 {
-		userNetworks = append(userNetworks, NO_NETWORKS_PRESENT)
-	}
-	return nil, userNetworks, username
-}
-
-//Consider a more secure way of setting master key
-func authenticateMaster(tokenString string) bool {
-	return tokenString == servercfg.GetMasterKey()
-}
-
-//Consider a more secure way of setting master key
-func authenticateDNSToken(tokenString string) bool {
-	tokens := strings.Split(tokenString, " ")
-	if len(tokens) < 2 {
-		return false
-	}
-	return tokens[1] == servercfg.GetDNSKey()
-}
-
 //simple get all networks function
 func getNetworks(w http.ResponseWriter, r *http.Request) {
 

+ 48 - 34
controllers/security.go

@@ -14,10 +14,7 @@ import (
 	"github.com/gravitl/netmaker/servercfg"
 )
 
-//Security check DNS is middleware for every DNS function and just checks to make sure that its the master or dns token calling
-//Only admin should have access to all these network-level actions
-//DNS token should have access to only read functions
-func securityCheck(reqAdmin bool, allowDNSToken bool, next http.Handler) http.HandlerFunc {
+func securityCheck(reqAdmin bool, next http.Handler) http.HandlerFunc {
 	return func(w http.ResponseWriter, r *http.Request) {
 		var errorResponse = models.ErrorResponse{
 			Code: http.StatusUnauthorized, Message: "W1R3: It's not you it's me.",
@@ -25,42 +22,14 @@ func securityCheck(reqAdmin bool, allowDNSToken bool, next http.Handler) http.Ha
 
 		var params = mux.Vars(r)
 		bearerToken := r.Header.Get("Authorization")
-		if allowDNSToken && authenticateDNSToken(bearerToken) {
+		if strings.Contains(r.RequestURI, "/dns") && strings.ToUpper(r.Method) == "GET" && authenticateDNSToken(bearerToken) {
+			// do dns stuff
 			r.Header.Set("user", "nameserver")
 			networks, _ := json.Marshal([]string{ALL_NETWORK_ACCESS})
 			r.Header.Set("networks", string(networks))
 			next.ServeHTTP(w, r)
-		} else {
-			err, networks, username := SecurityCheck(reqAdmin, params["networkname"], bearerToken)
-			if err != nil {
-				if strings.Contains(err.Error(), "does not exist") {
-					errorResponse.Code = http.StatusNotFound
-				}
-				errorResponse.Message = err.Error()
-				returnErrorResponse(w, r, errorResponse)
-				return
-			}
-			networksJson, err := json.Marshal(&networks)
-			if err != nil {
-				errorResponse.Message = err.Error()
-				returnErrorResponse(w, r, errorResponse)
-				return
-			}
-			r.Header.Set("user", username)
-			r.Header.Set("networks", string(networksJson))
-			next.ServeHTTP(w, r)
-		}
-	}
-}
-
-func securityCheck(reqAdmin bool, next http.Handler) http.HandlerFunc {
-	return func(w http.ResponseWriter, r *http.Request) {
-		var errorResponse = models.ErrorResponse{
-			Code: http.StatusUnauthorized, Message: "W1R3: It's not you it's me.",
 		}
 
-		var params = mux.Vars(r)
-		bearerToken := r.Header.Get("Authorization")
 		err, networks, username := SecurityCheck(reqAdmin, params["networkname"], bearerToken)
 		if err != nil {
 			if strings.Contains(err.Error(), "does not exist") {
@@ -141,3 +110,48 @@ func authenticateDNSToken(tokenString string) bool {
 	}
 	return tokens[1] == servercfg.GetDNSKey()
 }
+
+// ValidateUserToken - self explained
+func ValidateUserToken(token string, user string, adminonly bool) error {
+	var tokenSplit = strings.Split(token, " ")
+	//I put this in in case the user doesn't put in a token at all (in which case it's empty)
+	//There's probably a smarter way of handling this.
+	var authToken = "928rt238tghgwe@TY@$Y@#WQAEGB2FC#@HG#@$Hddd"
+
+	if len(tokenSplit) > 1 {
+		authToken = tokenSplit[1]
+	} else {
+		return errors.New("Missing Auth Token.")
+	}
+
+	username, _, isadmin, err := logic.VerifyUserToken(authToken)
+	if err != nil {
+		return errors.New("Error Verifying Auth Token")
+	}
+	isAuthorized := false
+	if adminonly {
+		isAuthorized = isadmin
+	} else {
+		isAuthorized = username == user || isadmin
+	}
+	if !isAuthorized {
+		return errors.New("You are unauthorized to access this endpoint.")
+	}
+
+	return nil
+}
+
+func continueIfUserMatch(next http.Handler) http.HandlerFunc {
+	return func(w http.ResponseWriter, r *http.Request) {
+		var errorResponse = models.ErrorResponse{
+			Code: http.StatusUnauthorized, Message: "W1R3: This doesn't look like you.",
+		}
+		var params = mux.Vars(r)
+		var requestedUser = params["username"]
+		if requestedUser != r.Header.Get("user") {
+			returnErrorResponse(w, r, errorResponse)
+			return
+		}
+		next.ServeHTTP(w, r)
+	}
+}

+ 7 - 81
controllers/userHttpController.go

@@ -5,7 +5,6 @@ import (
 	"errors"
 	"fmt"
 	"net/http"
-	"strings"
 
 	"github.com/gorilla/mux"
 	"github.com/gravitl/netmaker/auth"
@@ -20,13 +19,13 @@ func userHandlers(r *mux.Router) {
 	r.HandleFunc("/api/users/adm/hasadmin", hasAdmin).Methods("GET")
 	r.HandleFunc("/api/users/adm/createadmin", createAdmin).Methods("POST")
 	r.HandleFunc("/api/users/adm/authenticate", authenticateUser).Methods("POST")
-	r.HandleFunc("/api/users/{username}", authorizeUser(http.HandlerFunc(updateUser))).Methods("PUT")
-	r.HandleFunc("/api/users/networks/{username}", authorizeUserAdm(http.HandlerFunc(updateUserNetworks))).Methods("PUT")
-	r.HandleFunc("/api/users/{username}/adm", authorizeUserAdm(http.HandlerFunc(updateUserAdm))).Methods("PUT")
-	r.HandleFunc("/api/users/{username}", authorizeUserAdm(http.HandlerFunc(createUser))).Methods("POST")
-	r.HandleFunc("/api/users/{username}", authorizeUser(http.HandlerFunc(deleteUser))).Methods("DELETE")
-	r.HandleFunc("/api/users/{username}", authorizeUser(http.HandlerFunc(getUser))).Methods("GET")
-	r.HandleFunc("/api/users", authorizeUserAdm(http.HandlerFunc(getUsers))).Methods("GET")
+	r.HandleFunc("/api/users/{username}", securityCheck(false, continueIfUserMatch(http.HandlerFunc(updateUser)))).Methods("PUT")
+	r.HandleFunc("/api/users/networks/{username}", securityCheck(true, http.HandlerFunc(updateUserNetworks))).Methods("PUT")
+	r.HandleFunc("/api/users/{username}/adm", securityCheck(true, http.HandlerFunc(updateUserAdm))).Methods("PUT")
+	r.HandleFunc("/api/users/{username}", securityCheck(true, http.HandlerFunc(createUser))).Methods("POST")
+	r.HandleFunc("/api/users/{username}", securityCheck(false, continueIfUserMatch(http.HandlerFunc(deleteUser)))).Methods("DELETE")
+	r.HandleFunc("/api/users/{username}", securityCheck(false, continueIfUserMatch(http.HandlerFunc(getUser)))).Methods("GET")
+	r.HandleFunc("/api/users", securityCheck(true, http.HandlerFunc(getUsers))).Methods("GET")
 	r.HandleFunc("/api/oauth/login", auth.HandleAuthLogin).Methods("GET")
 	r.HandleFunc("/api/oauth/callback", auth.HandleAuthCallback).Methods("GET")
 }
@@ -82,79 +81,6 @@ func authenticateUser(response http.ResponseWriter, request *http.Request) {
 	response.Write(successJSONResponse)
 }
 
-// The middleware for most requests to the API
-// They all pass  through here first
-// This will validate the JWT (or check for master token)
-// This will also check against the authNetwork and make sure the node should be accessing that endpoint,
-// even if it's technically ok
-// This is kind of a poor man's RBAC. There's probably a better/smarter way.
-// TODO: Consider better RBAC implementations
-func authorizeUser(next http.Handler) http.HandlerFunc {
-	return func(w http.ResponseWriter, r *http.Request) {
-		w.Header().Set("Content-Type", "application/json")
-		var params = mux.Vars(r)
-
-		// get the auth token
-		bearerToken := r.Header.Get("Authorization")
-		username := params["username"]
-		err := ValidateUserToken(bearerToken, username, false)
-		if err != nil {
-			returnErrorResponse(w, r, formatError(err, "unauthorized"))
-			return
-		}
-		r.Header.Set("user", username)
-		next.ServeHTTP(w, r)
-	}
-}
-
-func authorizeUserAdm(next http.Handler) http.HandlerFunc {
-	return func(w http.ResponseWriter, r *http.Request) {
-		w.Header().Set("Content-Type", "application/json")
-		var params = mux.Vars(r)
-
-		//get the auth token
-		bearerToken := r.Header.Get("Authorization")
-		username := params["username"]
-		err := ValidateUserToken(bearerToken, username, true)
-		if err != nil {
-			returnErrorResponse(w, r, formatError(err, "unauthorized"))
-			return
-		}
-		r.Header.Set("user", username)
-		next.ServeHTTP(w, r)
-	}
-}
-
-// ValidateUserToken - self explained
-func ValidateUserToken(token string, user string, adminonly bool) error {
-	var tokenSplit = strings.Split(token, " ")
-	//I put this in in case the user doesn't put in a token at all (in which case it's empty)
-	//There's probably a smarter way of handling this.
-	var authToken = "928rt238tghgwe@TY@$Y@#WQAEGB2FC#@HG#@$Hddd"
-
-	if len(tokenSplit) > 1 {
-		authToken = tokenSplit[1]
-	} else {
-		return errors.New("Missing Auth Token.")
-	}
-
-	username, _, isadmin, err := logic.VerifyUserToken(authToken)
-	if err != nil {
-		return errors.New("Error Verifying Auth Token")
-	}
-	isAuthorized := false
-	if adminonly {
-		isAuthorized = isadmin
-	} else {
-		isAuthorized = username == user || isadmin
-	}
-	if !isAuthorized {
-		return errors.New("You are unauthorized to access this endpoint.")
-	}
-
-	return nil
-}
-
 func hasAdmin(w http.ResponseWriter, r *http.Request) {
 
 	w.Header().Set("Content-Type", "application/json")