From fe915d931d7fa7cef13cbd3baced0b99866ee696 Mon Sep 17 00:00:00 2001 From: Alex <31018251+afeiszli@users.noreply.github.com> Date: Thu, 3 Jun 2021 00:40:28 -0400 Subject: [PATCH 01/12] Update netclient-install.sh --- scripts/netclient-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/netclient-install.sh b/scripts/netclient-install.sh index 6ec28b36..27e0305b 100755 --- a/scripts/netclient-install.sh +++ b/scripts/netclient-install.sh @@ -3,7 +3,7 @@ set -e [ -z "$KEY" ] && KEY=nokey; -wget -O netclient https://github.com/gravitl/netmaker/releases/download/v0.5/netclient +wget -O netclient https://github.com/gravitl/netmaker/releases/download/v0.5-beta/netclient chmod +x netclient sudo ./netclient register -t $KEY sudo ./netclient join -t $KEY From 40023b2c09052c0b85e1b8a4c11ddea052311dc0 Mon Sep 17 00:00:00 2001 From: Alex <31018251+afeiszli@users.noreply.github.com> Date: Tue, 8 Jun 2021 07:18:09 -0400 Subject: [PATCH 02/12] Update docker-compose.slim.yml --- compose/docker-compose.slim.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compose/docker-compose.slim.yml b/compose/docker-compose.slim.yml index 4c4993f5..76eba831 100644 --- a/compose/docker-compose.slim.yml +++ b/compose/docker-compose.slim.yml @@ -22,7 +22,7 @@ services: - "50051:50051" depends_on: - mongodb - image: gravitl/netmaker:v0.3 + image: gravitl/netmaker:v0.5 restart: always environment: SERVER_HOST: "HOST_IP" @@ -33,7 +33,7 @@ services: container_name: netmaker-ui depends_on: - netmaker - image: gravitl/netmaker-ui:v0.3 + image: gravitl/netmaker-ui:v0.5 links: - "netmaker:api" ports: From 0875fae57f1321457c0be29ab0f3d22006480747 Mon Sep 17 00:00:00 2001 From: Alex <31018251+afeiszli@users.noreply.github.com> Date: Tue, 8 Jun 2021 07:18:46 -0400 Subject: [PATCH 03/12] Update docker-compose.reference.yml --- compose/docker-compose.reference.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compose/docker-compose.reference.yml b/compose/docker-compose.reference.yml index 8a928d23..93d53e33 100644 --- a/compose/docker-compose.reference.yml +++ b/compose/docker-compose.reference.yml @@ -17,7 +17,7 @@ services: container_name: netmaker depends_on: - mongodb - image: gravitl/netmaker:v0.3 + image: gravitl/netmaker:v0.5 volumes: # Volume mounts necessary for CLIENT_MODE to control netclient, wireguard, and networking on host (except dnsconfig, which is where dns config files are stored for use by CoreDNS) - ./:/local - /etc/netclient:/etc/netclient @@ -61,7 +61,7 @@ services: container_name: netmaker-ui depends_on: - netmaker - image: gravitl/netmaker-ui:v0.3 + image: gravitl/netmaker-ui:v0.5 links: - "netmaker:api" ports: From f8c91c8482c653f63ccfc45cbe8dc3200fa3385a Mon Sep 17 00:00:00 2001 From: Alex <31018251+afeiszli@users.noreply.github.com> Date: Tue, 8 Jun 2021 07:23:14 -0400 Subject: [PATCH 04/12] Update docker-compose.slim.yml --- compose/docker-compose.slim.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compose/docker-compose.slim.yml b/compose/docker-compose.slim.yml index 4c4993f5..76eba831 100644 --- a/compose/docker-compose.slim.yml +++ b/compose/docker-compose.slim.yml @@ -22,7 +22,7 @@ services: - "50051:50051" depends_on: - mongodb - image: gravitl/netmaker:v0.3 + image: gravitl/netmaker:v0.5 restart: always environment: SERVER_HOST: "HOST_IP" @@ -33,7 +33,7 @@ services: container_name: netmaker-ui depends_on: - netmaker - image: gravitl/netmaker-ui:v0.3 + image: gravitl/netmaker-ui:v0.5 links: - "netmaker:api" ports: From fb1e7e75b708245ad24b6a21a2d49228f9f673b0 Mon Sep 17 00:00:00 2001 From: Alex <31018251+afeiszli@users.noreply.github.com> Date: Tue, 8 Jun 2021 07:23:53 -0400 Subject: [PATCH 05/12] Update docker-compose.reference.yml --- compose/docker-compose.reference.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compose/docker-compose.reference.yml b/compose/docker-compose.reference.yml index 8a928d23..93d53e33 100644 --- a/compose/docker-compose.reference.yml +++ b/compose/docker-compose.reference.yml @@ -17,7 +17,7 @@ services: container_name: netmaker depends_on: - mongodb - image: gravitl/netmaker:v0.3 + image: gravitl/netmaker:v0.5 volumes: # Volume mounts necessary for CLIENT_MODE to control netclient, wireguard, and networking on host (except dnsconfig, which is where dns config files are stored for use by CoreDNS) - ./:/local - /etc/netclient:/etc/netclient @@ -61,7 +61,7 @@ services: container_name: netmaker-ui depends_on: - netmaker - image: gravitl/netmaker-ui:v0.3 + image: gravitl/netmaker-ui:v0.5 links: - "netmaker:api" ports: From 0f03955819dda28d11899ed5f2f12a68e69ae0f2 Mon Sep 17 00:00:00 2001 From: Alex <31018251+afeiszli@users.noreply.github.com> Date: Tue, 8 Jun 2021 17:06:28 -0400 Subject: [PATCH 06/12] Create netclient-install.slim.sh --- scripts/netclient-install.slim.sh | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 scripts/netclient-install.slim.sh diff --git a/scripts/netclient-install.slim.sh b/scripts/netclient-install.slim.sh new file mode 100644 index 00000000..75fc2025 --- /dev/null +++ b/scripts/netclient-install.slim.sh @@ -0,0 +1,14 @@ +#!/bin/sh +set -e + +if [[ $EUID -ne 0 ]]; then + echo "This script must be run as root" + exit 1 +fi + +[ -z "$KEY" ] && KEY=nokey; + +wget -O netclient https://github.com/gravitl/netmaker/releases/download/v0.5-beta/netclient +chmod +x netclient +sudo ./netclient join -t $KEY +rm -f netclient From 0164345f85cece8bb89917095c4efafe78cd8b1e Mon Sep 17 00:00:00 2001 From: Alex <31018251+afeiszli@users.noreply.github.com> Date: Tue, 8 Jun 2021 17:09:47 -0400 Subject: [PATCH 07/12] Update docker-compose.slim.yml --- compose/docker-compose.slim.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/compose/docker-compose.slim.yml b/compose/docker-compose.slim.yml index 76eba831..3db9f371 100644 --- a/compose/docker-compose.slim.yml +++ b/compose/docker-compose.slim.yml @@ -28,6 +28,7 @@ services: SERVER_HOST: "HOST_IP" DNS_MODE: "off" CLIENT_MODE: "off" + MONGO_HOST: "mongodb" SERVER_GRPC_WIREGUARD: "off" netmaker-ui: container_name: netmaker-ui From 041b641e17a295fd2c32a6e5d5c6a520af6a9157 Mon Sep 17 00:00:00 2001 From: afeiszli Date: Fri, 2 Jul 2021 00:03:46 -0400 Subject: [PATCH 08/12] multitenancy working --- controllers/.userHttpController.go.swp | Bin 0 -> 16384 bytes controllers/networkHttpController.go | 11 +- controllers/nodeHttpController.go | 22 +++- controllers/serverHttpController.go | 2 +- controllers/userHttpController.go | 151 ++++++++++++++++++++++--- functions/helpers.go | 10 ++ functions/jwt.go | 13 ++- models/structs.go | 2 + 8 files changed, 184 insertions(+), 27 deletions(-) create mode 100644 controllers/.userHttpController.go.swp diff --git a/controllers/.userHttpController.go.swp b/controllers/.userHttpController.go.swp new file mode 100644 index 0000000000000000000000000000000000000000..f3d93afa76ec991b8b2c3fb4da3b08e99492fa2c GIT binary patch literal 16384 zcmeHNL2M*P6)i{>m<_uLa6 z>T27=%8&!f!fAtqLT({Gazq)>P>-5F>1^%En!SK0@vG~fHU;X6A6?^=FBP>Y7HWeTA*^+7_ z(}9jbCMVDl)pAo|L>yyn`Qf=gR-B074;7l_18!0i{kpLW}s%E zW}s%EW}s%EW}s%EW}s%EW}s%EW}s%^T`&;fz(0m|uhDrM_y6Vj|L=D)_BY^d;CH|; zfu92}11|#41K$Il1rlHxXactZZybjl@Esrm&H*#PG{AvR0lz=S*mr?h;7=cBEC-GQ zFWkY{9Prnpj9ml%0z3=M19QMX8;t!3_%`rm;5cvxa1?m!LyWxyJO?}l%mH5n9s)iO z+zGt%LB_5EKLmaNyZ}50%mddx0K0%UfY*T+foFgP;1j@6;Mx(!{s_DYyaId+xC|hT z!X5;CU>x|z?Tq~jcoldB*Z~AE1AGLyej7Lfehl0PTt@=tdEhKC1xx@OcpdXXbMxCG zA9A=UZtSo|LkXR$lsnEi7fMN$d5dDpbuQo?3-0hw^B~vj%i_sgWZI*`#h@!t?C?}- z-j;b9IZf7x+tiw$IK$I8;g?t=5^X35gESP0+hnY<87L!ihKKYTM@a91c<16YI-d$R z)Z62{m7!$el0&a|r0B(&+_UqYrZ*!(8433uC9*ux=-#xqo$*QPA=P4Aduy>?UyZv$ z=GraG&IaK|N0HrbbDU3{9@7x;R-iGBNK<7TDeB6!BTGfkeA>Qk%zp1_K6KzKq^0cZA~fd3i_D|V_Mv$V2G z?QW@i9HTW0t1z$>K=67&|^B374g2-a??V^&lz7Hl&!`GL3i(tANAH z_`_?f)>U@KVQr(Flu3TW{FlW>h8SLz8zPC?~JUjmgYRHKS zmdY+UEo9?Qg8*;t* zVvbjY!t}yMsmA%Jo6qZeGZW7HGRHLHAM8Bj&Bfk02oVF1HL6M?uy#mv$n?^&X_?Uq zv5}8D;&hED3LVlx8p{|(n`#zk zI6DBg3nJXcu!iS`(gOb}<2b%<#Qz zr-OpI?=NCmn+{5K8P7A?M`PI8D|b0sQCN{G7MlY0P|P!1a%q_f-yZD^6j11+yg9Sj z&SC3u9Y5&B=B3jNA-`-S4CPVRHC9=1W#l_TQ(e|DVLKb`X!pzvdby=8FbIEmQ4}4OPHhMpxYB|2kUC2(e#I%Y+5a%| zI34oILn&GhvQi6vj;gS3ZI?B9aIp?DSMjlKzU?jJ(HpZ%PhRkTrz4Xf?RZjk{B4-n z?`i3`%s1iZni};u%fe*$;RmfAlBi!+)_&)$Y0j>fs0hfWN0S#op7(i$KBf#yz&v35)DR3Eh z8fXCl@CYyod=B_5a1U@da2IeKI0pO%d4OL4&j43|r+_vffCqrj04IU#$Pv5+yb4I5 z4TL}oxEnYLIKZDVp0|Nt11|$tf$sxd;4xqgSOqB8Kx3Z;>QBu;%|OjS%|OjS%|OjS z%|Ok-;TSNvXLH+v2l688ij-IbPwCzg64g3MU4dg#gYFByf&?3-qZAHI$j>X~xz*#p zchMdMa27K8A*8yI8XC>gu?AX^icRE4RUT@WPH51-X$s4;gt_lQV#}u4ZK*kvyRs*g z$&K(bk4_4PQ%+4X4942TdYL(!r9%eNI$KT8{&agLA^+ImYUgotg=7;Ak%yeZA<~3f zNCk~k;%3j>>x@ZSAg@{^f028J-rc#(!3D>OLXx;EGQZUiLu;9ca`)_cG_$*#{z8A1 zDYYijX?_nvhyqIPS*j=jh~x7`scPAKnyChytRAZu8qw0QQos-&f{L;Y2kfh1TZ5N`Pb zv1BA7HSGYGa>Ww0l$bfAK8v-9%dVD`iJ?Z?gv%hEdGm!y8k)@HJn?7HqAal&8SKCK C#sDq= literal 0 HcmV?d00001 diff --git a/controllers/networkHttpController.go b/controllers/networkHttpController.go index 4e5f3886..a0174715 100644 --- a/controllers/networkHttpController.go +++ b/controllers/networkHttpController.go @@ -78,8 +78,15 @@ func SecurityCheck(netname, token string) error { } //all endpoints here require master so not as complicated if !hasBearer || !authenticateMaster(authToken) { - _, isadmin, err := functions.VerifyUserToken(authToken) - if err != nil || !isadmin { + _, networks, isadmin, err := functions.VerifyUserToken(authToken) + if err != nil { + return errors.New("Error verifying user token") + } + if !isadmin && netname != ""{ + if !functions.SliceContains(networks, netname){ + return errors.New("You are unauthorized to access this endpoint") + } + } else if !isadmin { return errors.New("You are unauthorized to access this endpoint") } } diff --git a/controllers/nodeHttpController.go b/controllers/nodeHttpController.go index 6d7c4d71..d12bcb43 100644 --- a/controllers/nodeHttpController.go +++ b/controllers/nodeHttpController.go @@ -186,8 +186,9 @@ func authorize(networkCheck bool, authNetwork string, next http.Handler) http.Ha var isAuthorized = false var macaddress = "" - _, isadmin, errN := functions.VerifyUserToken(authToken) - if errN == nil && isadmin { + _, networks, isadmin, errN := functions.VerifyUserToken(authToken) + isnetadmin := isadmin + if errN == nil && isadmin { macaddress = "mastermac" isAuthorized = true } else { @@ -201,6 +202,11 @@ func authorize(networkCheck bool, authNetwork string, next http.Handler) http.Ha } macaddress = mac } + if !isadmin && params["network"] != ""{ + if functions.SliceContains(networks, params["network"]){ + isnetadmin = true + } + } //The mastermac (login with masterkey from config) can do everything!! May be dangerous. if macaddress == "mastermac" { isAuthorized = true @@ -212,8 +218,11 @@ func authorize(networkCheck bool, authNetwork string, next http.Handler) http.Ha case "all": isAuthorized = true case "nodes": - isAuthorized = (macaddress != "") + isAuthorized = (macaddress != "") || isnetadmin case "network": + if isnetadmin { + isAuthorized = true + } else { node, err := functions.GetNodeByMacAddress(params["network"], macaddress) if err != nil { errorResponse = models.ErrorResponse{ @@ -223,8 +232,13 @@ func authorize(networkCheck bool, authNetwork string, next http.Handler) http.Ha return } isAuthorized = (node.Network == params["network"]) + } case "node": - isAuthorized = (macaddress == params["macaddress"]) + if isnetadmin { + isAuthorized = true + } else { + isAuthorized = (macaddress == params["macaddress"]) + } case "master": isAuthorized = (macaddress == "mastermac") default: diff --git a/controllers/serverHttpController.go b/controllers/serverHttpController.go index 9a1885e0..cb5ce49b 100644 --- a/controllers/serverHttpController.go +++ b/controllers/serverHttpController.go @@ -42,7 +42,7 @@ func securityCheckServer(next http.Handler) http.HandlerFunc { } //all endpoints here require master so not as complicated //still might not be a good way of doing this - _, isadmin, _ := functions.VerifyUserToken(authToken) + _, _, isadmin, _ := functions.VerifyUserToken(authToken) if !isadmin && !authenticateMasterServer(authToken) { errorResponse = models.ErrorResponse{ diff --git a/controllers/userHttpController.go b/controllers/userHttpController.go index 1645859f..1cd23b7f 100644 --- a/controllers/userHttpController.go +++ b/controllers/userHttpController.go @@ -26,8 +26,11 @@ func userHandlers(r *mux.Router) { 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/{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") } //Node authenticates using its password and retrieves a JWT for authorization. @@ -96,9 +99,9 @@ func VerifyAuthRequest(authRequest models.UserAuthParams) (string, error) { return "", errors.New("User " + authRequest.UserName + " not found") } // This is a a useless test as cannot create user that is not an an admin - if !result.IsAdmin { - return "", errors.New("User is not an admin") - } + //if !result.IsAdmin { + // return "", errors.New("User is not an admin") + //} //compare password from request to stored password in database //might be able to have a common hash (certificates?) and compare those so that a password isn't passed in in plain text... @@ -109,7 +112,7 @@ func VerifyAuthRequest(authRequest models.UserAuthParams) (string, error) { } //Create a new JWT for the node - tokenString, _ := functions.CreateUserJWT(authRequest.UserName, true) + tokenString, _ := functions.CreateUserJWT(authRequest.UserName, result.Networks, result.IsAdmin) return tokenString, nil } @@ -123,10 +126,11 @@ func VerifyAuthRequest(authRequest models.UserAuthParams) (string, error) { 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") - err := ValidateUserToken(bearerToken) + err := ValidateUserToken(bearerToken, params["username"], false) if err != nil { returnErrorResponse(w, r, formatError(err, "unauthorized")) return @@ -135,7 +139,24 @@ func authorizeUser(next http.Handler) http.HandlerFunc { } } -func ValidateUserToken(token string) error { +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") + err := ValidateUserToken(bearerToken, params["username"], true) + if err != nil { + returnErrorResponse(w, r, formatError(err, "unauthorized")) + return + } + next.ServeHTTP(w, r) + } +} + + +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) @@ -148,12 +169,16 @@ func ValidateUserToken(token string) error { return errors.New("Missing Auth Token.") } - username, _, err := functions.VerifyUserToken(authToken) + username, _, isadmin, err := functions.VerifyUserToken(authToken) if err != nil { return errors.New("Error Verifying Auth Token") } - - isAuthorized := username != "" + isAuthorized := false + if adminonly { + isAuthorized = isadmin + } else { + isAuthorized = username == user || isadmin + } if !isAuthorized { return errors.New("You are unauthorized to access this endpoint.") } @@ -214,6 +239,42 @@ func GetUser(username string) (models.User, error) { return user, err } +func GetUsers() ([]models.User, error) { + + var users []models.User + + collection := mongoconn.Client.Database("netmaker").Collection("users") + + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + + cur, err := collection.Find(ctx, bson.M{}, options.Find().SetProjection(bson.M{"_id": 0})) + + if err != nil { + return users, err + } + + defer cancel() + + for cur.Next(context.TODO()) { + + var user models.User + err := cur.Decode(&user) + if err != nil { + return users, err + } + + // add network our array + users = append(users, user) + } + + if err := cur.Err(); err != nil { + return users, err + } + + return users, err +} + + //Get an individual node. Nothin fancy here folks. func getUser(w http.ResponseWriter, r *http.Request) { // set header. @@ -231,13 +292,27 @@ func getUser(w http.ResponseWriter, r *http.Request) { json.NewEncoder(w).Encode(user) } +//Get an individual node. Nothin fancy here folks. +func getUsers(w http.ResponseWriter, r *http.Request) { + // set header. + w.Header().Set("Content-Type", "application/json") + + users, err := GetUsers() + + if err != nil { + returnErrorResponse(w, r, formatError(err, "internal")) + return + } + + json.NewEncoder(w).Encode(users) +} + + func CreateUser(user models.User) (models.User, error) { hasadmin, err := HasAdmin() - if hasadmin { + if hasadmin && user.IsAdmin { return models.User{}, errors.New("Admin already Exists") } - - user.IsAdmin = true err = ValidateUser("create", user) if err != nil { return models.User{}, err @@ -251,7 +326,7 @@ func CreateUser(user models.User) (models.User, error) { //set password to encrypted password user.Password = string(hash) - tokenString, _ := functions.CreateUserJWT(user.UserName, user.IsAdmin) + tokenString, _ := functions.CreateUserJWT(user.UserName,user.Networks, user.IsAdmin) if tokenString == "" { //returnErrorResponse(w, r, errorResponse) @@ -275,7 +350,7 @@ func createAdmin(w http.ResponseWriter, r *http.Request) { var admin models.User //get node from body of request _ = json.NewDecoder(r.Body).Decode(&admin) - + admin.IsAdmin = true admin, err := CreateUser(admin) if err != nil { @@ -286,6 +361,24 @@ func createAdmin(w http.ResponseWriter, r *http.Request) { json.NewEncoder(w).Encode(admin) } +func createUser(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + + var user models.User + //get node from body of request + _ = json.NewDecoder(r.Body).Decode(&user) + + user, err := CreateUser(user) + + if err != nil { + returnErrorResponse(w, r, formatError(err, "badrequest")) + return + } + + json.NewEncoder(w).Encode(user) +} + + func UpdateUser(userchange models.User, user models.User) (models.User, error) { err := ValidateUser("update", userchange) @@ -298,6 +391,9 @@ func UpdateUser(userchange models.User, user models.User) (models.User, error) { if userchange.UserName != "" { user.UserName = userchange.UserName } + if len(userchange.Networks) > 0 { + user.Networks = userchange.Networks + } if userchange.Password != "" { //encrypt that password so we never see it again hash, err := bcrypt.GenerateFromPassword([]byte(userchange.Password), 5) @@ -325,6 +421,7 @@ func UpdateUser(userchange models.User, user models.User) (models.User, error) { {"$set", bson.D{ {"username", user.UserName}, {"password", user.Password}, + {"networks", user.Networks}, {"isadmin", user.IsAdmin}, }}, } @@ -360,6 +457,7 @@ func updateUser(w http.ResponseWriter, r *http.Request) { returnErrorResponse(w, r, formatError(err, "internal")) return } + userchange.Networks = nil user, err = UpdateUser(userchange, user) if err != nil { returnErrorResponse(w, r, formatError(err, "badrequest")) @@ -368,6 +466,31 @@ func updateUser(w http.ResponseWriter, r *http.Request) { json.NewEncoder(w).Encode(user) } +func updateUserAdm(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + var params = mux.Vars(r) + var user models.User + //start here + user, err := GetUser(params["username"]) + if err != nil { + returnErrorResponse(w, r, formatError(err, "internal")) + return + } + var userchange models.User + // we decode our body request params + err = json.NewDecoder(r.Body).Decode(&userchange) + if err != nil { + returnErrorResponse(w, r, formatError(err, "internal")) + return + } + user, err = UpdateUser(userchange, user) + if err != nil { + returnErrorResponse(w, r, formatError(err, "badrequest")) + return + } + json.NewEncoder(w).Encode(user) +} + func DeleteUser(user string) (bool, error) { deleted := false diff --git a/functions/helpers.go b/functions/helpers.go index d95d4c52..b2c9215f 100644 --- a/functions/helpers.go +++ b/functions/helpers.go @@ -26,6 +26,16 @@ import ( //Takes in an arbitrary field and value for field and checks to see if any other //node has that value for the same field within the network +func SliceContains(slice []string, item string) bool { + set := make(map[string]struct{}, len(slice)) + for _, s := range slice { + set[s] = struct{}{} + } + + _, ok := set[item] + return ok +} + func CreateServerToken(netID string) (string, error) { var network models.Network var accesskey models.AccessKey diff --git a/functions/jwt.go b/functions/jwt.go index 5beee30c..225232bf 100644 --- a/functions/jwt.go +++ b/functions/jwt.go @@ -28,11 +28,12 @@ func CreateJWT(macaddress string, network string) (response string, err error) { return "", err } -func CreateUserJWT(username string, isadmin bool) (response string, err error) { +func CreateUserJWT(username string, networks []string, isadmin bool) (response string, err error) { expirationTime := time.Now().Add(60 * time.Minute) claims := &models.UserClaims{ UserName: username, - IsAdmin: isadmin, + Networks: networks, + IsAdmin: isadmin, StandardClaims: jwt.StandardClaims{ ExpiresAt: expirationTime.Unix(), }, @@ -47,11 +48,11 @@ func CreateUserJWT(username string, isadmin bool) (response string, err error) { } // VerifyToken func will used to Verify the JWT Token while using APIS -func VerifyUserToken(tokenString string) (username string, isadmin bool, err error) { +func VerifyUserToken(tokenString string) (username string, networks []string, isadmin bool, err error) { claims := &models.UserClaims{} if tokenString == servercfg.GetMasterKey() { - return "masteradministrator", true, nil + return "masteradministrator", nil, true, nil } token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) { @@ -59,9 +60,9 @@ func VerifyUserToken(tokenString string) (username string, isadmin bool, err err }) if token != nil { - return claims.UserName, claims.IsAdmin, nil + return claims.UserName, claims.Networks, claims.IsAdmin, nil } - return "", false, err + return "", nil, false, err } // VerifyToken func will used to Verify the JWT Token while using APIS diff --git a/models/structs.go b/models/structs.go index 1a8b7c09..37e27e6f 100644 --- a/models/structs.go +++ b/models/structs.go @@ -10,6 +10,7 @@ type AuthParams struct { type User struct { UserName string `json:"username" bson:"username" validate:"alphanum,min=3"` Password string `json:"password" bson:"password" validate:"required,min=5"` + Networks []string `json:"networks" bson:"networks"` IsAdmin bool `json:"isadmin" bson:"isadmin"` } @@ -21,6 +22,7 @@ type UserAuthParams struct { type UserClaims struct { IsAdmin bool UserName string + Networks []string jwt.StandardClaims } From 20f6d5b6ac1cd9332ed5e964e4a924188f1fc8f2 Mon Sep 17 00:00:00 2001 From: afeiszli Date: Fri, 2 Jul 2021 00:03:54 -0400 Subject: [PATCH 09/12] multitenancy working --- controllers/.userHttpController.go.swp | Bin 16384 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 controllers/.userHttpController.go.swp diff --git a/controllers/.userHttpController.go.swp b/controllers/.userHttpController.go.swp deleted file mode 100644 index f3d93afa76ec991b8b2c3fb4da3b08e99492fa2c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16384 zcmeHNL2M*P6)i{>m<_uLa6 z>T27=%8&!f!fAtqLT({Gazq)>P>-5F>1^%En!SK0@vG~fHU;X6A6?^=FBP>Y7HWeTA*^+7_ z(}9jbCMVDl)pAo|L>yyn`Qf=gR-B074;7l_18!0i{kpLW}s%E zW}s%EW}s%EW}s%EW}s%EW}s%EW}s%^T`&;fz(0m|uhDrM_y6Vj|L=D)_BY^d;CH|; zfu92}11|#41K$Il1rlHxXactZZybjl@Esrm&H*#PG{AvR0lz=S*mr?h;7=cBEC-GQ zFWkY{9Prnpj9ml%0z3=M19QMX8;t!3_%`rm;5cvxa1?m!LyWxyJO?}l%mH5n9s)iO z+zGt%LB_5EKLmaNyZ}50%mddx0K0%UfY*T+foFgP;1j@6;Mx(!{s_DYyaId+xC|hT z!X5;CU>x|z?Tq~jcoldB*Z~AE1AGLyej7Lfehl0PTt@=tdEhKC1xx@OcpdXXbMxCG zA9A=UZtSo|LkXR$lsnEi7fMN$d5dDpbuQo?3-0hw^B~vj%i_sgWZI*`#h@!t?C?}- z-j;b9IZf7x+tiw$IK$I8;g?t=5^X35gESP0+hnY<87L!ihKKYTM@a91c<16YI-d$R z)Z62{m7!$el0&a|r0B(&+_UqYrZ*!(8433uC9*ux=-#xqo$*QPA=P4Aduy>?UyZv$ z=GraG&IaK|N0HrbbDU3{9@7x;R-iGBNK<7TDeB6!BTGfkeA>Qk%zp1_K6KzKq^0cZA~fd3i_D|V_Mv$V2G z?QW@i9HTW0t1z$>K=67&|^B374g2-a??V^&lz7Hl&!`GL3i(tANAH z_`_?f)>U@KVQr(Flu3TW{FlW>h8SLz8zPC?~JUjmgYRHKS zmdY+UEo9?Qg8*;t* zVvbjY!t}yMsmA%Jo6qZeGZW7HGRHLHAM8Bj&Bfk02oVF1HL6M?uy#mv$n?^&X_?Uq zv5}8D;&hED3LVlx8p{|(n`#zk zI6DBg3nJXcu!iS`(gOb}<2b%<#Qz zr-OpI?=NCmn+{5K8P7A?M`PI8D|b0sQCN{G7MlY0P|P!1a%q_f-yZD^6j11+yg9Sj z&SC3u9Y5&B=B3jNA-`-S4CPVRHC9=1W#l_TQ(e|DVLKb`X!pzvdby=8FbIEmQ4}4OPHhMpxYB|2kUC2(e#I%Y+5a%| zI34oILn&GhvQi6vj;gS3ZI?B9aIp?DSMjlKzU?jJ(HpZ%PhRkTrz4Xf?RZjk{B4-n z?`i3`%s1iZni};u%fe*$;RmfAlBi!+)_&)$Y0j>fs0hfWN0S#op7(i$KBf#yz&v35)DR3Eh z8fXCl@CYyod=B_5a1U@da2IeKI0pO%d4OL4&j43|r+_vffCqrj04IU#$Pv5+yb4I5 z4TL}oxEnYLIKZDVp0|Nt11|$tf$sxd;4xqgSOqB8Kx3Z;>QBu;%|OjS%|OjS%|OjS z%|Ok-;TSNvXLH+v2l688ij-IbPwCzg64g3MU4dg#gYFByf&?3-qZAHI$j>X~xz*#p zchMdMa27K8A*8yI8XC>gu?AX^icRE4RUT@WPH51-X$s4;gt_lQV#}u4ZK*kvyRs*g z$&K(bk4_4PQ%+4X4942TdYL(!r9%eNI$KT8{&agLA^+ImYUgotg=7;Ak%yeZA<~3f zNCk~k;%3j>>x@ZSAg@{^f028J-rc#(!3D>OLXx;EGQZUiLu;9ca`)_cG_$*#{z8A1 zDYYijX?_nvhyqIPS*j=jh~x7`scPAKnyChytRAZu8qw0QQos-&f{L;Y2kfh1TZ5N`Pb zv1BA7HSGYGa>Ww0l$bfAK8v-9%dVD`iJ?Z?gv%hEdGm!y8k)@HJn?7HqAal&8SKCK C#sDq= From 82e0bde9d1d8d25ff4ecf259b13ebe8d56a4c778 Mon Sep 17 00:00:00 2001 From: afeiszli Date: Fri, 2 Jul 2021 00:16:29 -0400 Subject: [PATCH 10/12] fix tests --- test/network_test.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/test/network_test.go b/test/network_test.go index 13067e41..ec306b64 100644 --- a/test/network_test.go +++ b/test/network_test.go @@ -26,7 +26,7 @@ func TestCreateNetwork(t *testing.T) { err = json.NewDecoder(response.Body).Decode(&message) assert.Nil(t, err, err) assert.Equal(t, http.StatusUnauthorized, message.Code) - assert.Contains(t, message.Message, "ou are unauthorized to access this endpoint") + assert.Contains(t, message.Message, "rror verifying user toke") }) t.Run("CreateNetwork", func(t *testing.T) { response, err := api(t, network, http.MethodPost, baseURL+"/api/networks", "secretkey") @@ -73,7 +73,7 @@ func TestGetNetworks(t *testing.T) { assert.Nil(t, err, err) assert.Equal(t, http.StatusUnauthorized, response.StatusCode) assert.Equal(t, http.StatusUnauthorized, message.Code) - assert.Contains(t, message.Message, "ou are unauthorized to access this endpoint") + assert.Contains(t, message.Message, "rror verifying user toke") }) } @@ -99,7 +99,7 @@ func TestGetNetwork(t *testing.T) { assert.Nil(t, err, err) assert.Equal(t, http.StatusUnauthorized, response.StatusCode) assert.Equal(t, http.StatusUnauthorized, message.Code) - assert.Contains(t, message.Message, "ou are unauthorized to access this endpoint") + assert.Contains(t, message.Message, "rror verifying user toke") }) t.Run("InvalidNetwork", func(t *testing.T) { response, err := api(t, "", http.MethodGet, baseURL+"/api/networks/badnetwork", "secretkey") @@ -125,7 +125,7 @@ func TestDeleteNetwork(t *testing.T) { assert.Nil(t, err, err) assert.Equal(t, http.StatusUnauthorized, response.StatusCode) assert.Equal(t, http.StatusUnauthorized, message.Code) - assert.Contains(t, message.Message, "You are unauthorized to access this endpoint") + assert.Contains(t, message.Message, "rror verifying user toke") }) t.Run("Badnetwork", func(t *testing.T) { response, err := api(t, "", http.MethodDelete, baseURL+"/api/networks/badnetwork", "secretkey") @@ -222,7 +222,7 @@ func TestCreateKey(t *testing.T) { err = json.NewDecoder(response.Body).Decode(&message) assert.Nil(t, err, err) assert.Equal(t, http.StatusUnauthorized, message.Code) - assert.Contains(t, message.Message, "ou are unauthorized to access this endpoint") + assert.Contains(t, message.Message, "rror verifying user toke") }) t.Run("Badnetwork", func(t *testing.T) { response, err := api(t, key, http.MethodPost, baseURL+"/api/networks/badnetwork/keys", "secretkey") @@ -277,7 +277,7 @@ func TestDeleteKey(t *testing.T) { err = json.NewDecoder(response.Body).Decode(&message) assert.Nil(t, err, err) assert.Equal(t, http.StatusUnauthorized, message.Code) - assert.Contains(t, message.Message, "ou are unauthorized to access this endpoint") + assert.Contains(t, message.Message, "rror verifying user toke") }) } @@ -314,7 +314,7 @@ func TestGetKeys(t *testing.T) { err = json.NewDecoder(response.Body).Decode(&message) assert.Nil(t, err, err) assert.Equal(t, http.StatusUnauthorized, message.Code) - assert.Contains(t, message.Message, "ou are unauthorized to access this endpoint") + assert.Contains(t, message.Message, "rror verifying user toke") }) } From 67e2eebb7dee7fb200589a0b18e0d6349c9ee2be Mon Sep 17 00:00:00 2001 From: afeiszli Date: Fri, 2 Jul 2021 00:22:08 -0400 Subject: [PATCH 11/12] fix tests --- controllers/userHttpController_test.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/controllers/userHttpController_test.go b/controllers/userHttpController_test.go index 6c03e46b..40470052 100644 --- a/controllers/userHttpController_test.go +++ b/controllers/userHttpController_test.go @@ -42,7 +42,7 @@ func TestMain(m *testing.M) { func TestHasAdmin(t *testing.T) { _, err := DeleteUser("admin") assert.Nil(t, err) - user := models.User{"admin", "password", true} + user := models.User{"admin", "password", nil, true} _, err = CreateUser(user) assert.Nil(t, err) t.Run("AdminExists", func(t *testing.T) { @@ -60,7 +60,7 @@ func TestHasAdmin(t *testing.T) { } func TestCreateUser(t *testing.T) { - user := models.User{"admin", "password", true} + user := models.User{"admin", "password", nil, true} t.Run("NoUser", func(t *testing.T) { _, err := DeleteUser("admin") assert.Nil(t, err) @@ -79,7 +79,7 @@ func TestDeleteUser(t *testing.T) { hasadmin, err := HasAdmin() assert.Nil(t, err) if !hasadmin { - user := models.User{"admin", "pasword", true} + user := models.User{"admin", "pasword", nil, true} _, err := CreateUser(user) assert.Nil(t, err) } @@ -138,7 +138,7 @@ func TestValidateUser(t *testing.T) { func TestGetUser(t *testing.T) { t.Run("UserExisits", func(t *testing.T) { - user := models.User{"admin", "password", true} + user := models.User{"admin", "password", nil, true} hasadmin, err := HasAdmin() assert.Nil(t, err) if !hasadmin { @@ -159,8 +159,8 @@ func TestGetUser(t *testing.T) { } func TestUpdateUser(t *testing.T) { - user := models.User{"admin", "password", true} - newuser := models.User{"hello", "world", true} + user := models.User{"admin", "password", nil, true} + newuser := models.User{"hello", "world", nil, true} t.Run("UserExisits", func(t *testing.T) { _, err := DeleteUser("admin") _, err = CreateUser(user) @@ -228,7 +228,7 @@ func TestVerifyAuthRequest(t *testing.T) { t.Run("Non-Admin", func(t *testing.T) { //can't create a user that is not a an admin t.Skip() - user := models.User{"admin", "admin", false} + user := models.User{"admin", "admin", nil, false} _, err := CreateUser(user) assert.Nil(t, err) authRequest := models.UserAuthParams{"admin", "admin"} @@ -239,7 +239,7 @@ func TestVerifyAuthRequest(t *testing.T) { }) t.Run("WrongPassword", func(t *testing.T) { _, err := DeleteUser("admin") - user := models.User{"admin", "password", true} + user := models.User{"admin", "password", nil, true} _, err = CreateUser(user) assert.Nil(t, err) authRequest := models.UserAuthParams{"admin", "badpass"} From 44244328796ecf7bb08b1be035649db965ac1144 Mon Sep 17 00:00:00 2001 From: afeiszli Date: Fri, 2 Jul 2021 00:28:57 -0400 Subject: [PATCH 12/12] fix tests --- controllers/userHttpController_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/controllers/userHttpController_test.go b/controllers/userHttpController_test.go index 40470052..5eee31ca 100644 --- a/controllers/userHttpController_test.go +++ b/controllers/userHttpController_test.go @@ -179,12 +179,12 @@ func TestUpdateUser(t *testing.T) { func TestValidateUserToken(t *testing.T) { t.Run("EmptyToken", func(t *testing.T) { - err := ValidateUserToken("") + err := ValidateUserToken("","",false) assert.NotNil(t, err) assert.Equal(t, "Missing Auth Token.", err.Error()) }) t.Run("InvalidToken", func(t *testing.T) { - err := ValidateUserToken("Bearer: badtoken") + err := ValidateUserToken("Bearer: badtoken","",false) assert.NotNil(t, err) assert.Equal(t, "Error Verifying Auth Token", err.Error()) }) @@ -193,7 +193,7 @@ func TestValidateUserToken(t *testing.T) { //need authorization }) t.Run("ValidToken", func(t *testing.T) { - err := ValidateUserToken("Bearer: secretkey") + err := ValidateUserToken("Bearer: secretkey","",true) assert.Nil(t, err) }) }