Browse Source

Merge branch 'develop' into dependabot/go_modules/github.com/go-playground/validator/v10-10.9.0

Alex 3 years ago
parent
commit
8eec7b1c60
63 changed files with 2431 additions and 1563 deletions
  1. 2 1
      .github/dependabot.yml
  2. 5 1
      README.md
  3. 157 0
      auth/auth.go
  4. 126 0
      auth/azure-ad.go
  5. 129 0
      auth/github.go
  6. 120 0
      auth/google.go
  7. 7 4
      compose/docker-compose.caddy.yml
  8. 8 5
      compose/docker-compose.nodns.yml
  9. 3 9
      compose/docker-compose.reference.yml
  10. 7 4
      compose/docker-compose.yml
  11. 35 32
      config/config.go
  12. 5 4
      controllers/authGrpc.go
  13. 4 4
      controllers/common.go
  14. 4 4
      controllers/controller.go
  15. 17 17
      controllers/dnsHttpController.go
  16. 13 14
      controllers/dnsHttpController_test.go
  17. 10 25
      controllers/extClientHttpController.go
  18. 12 12
      controllers/networkHttpController.go
  19. 2 1
      controllers/networkHttpController_test.go
  20. 5 5
      controllers/nodeGrpcController.go
  21. 27 23
      controllers/nodeHttpController.go
  22. 1 1
      controllers/nodeHttpController_test.go
  23. 5 4
      controllers/relay.go
  24. 2 2
      controllers/responseHttp.go
  25. 2 2
      controllers/serverHttpController.go
  26. 48 219
      controllers/userHttpController.go
  27. 46 45
      controllers/userHttpController_test.go
  28. 6 2
      database/database.go
  29. 2 2
      docs/architecture.rst
  30. 1 1
      docs/conf.py
  31. 3 423
      functions/helpers.go
  32. 5 33
      functions/local.go
  33. 4 3
      go.mod
  34. 5 3
      go.sum
  35. 62 0
      logic/accesskeys.go
  36. 267 0
      logic/auth.go
  37. 37 4
      logic/dns.go
  38. 28 3
      logic/extpeers.go
  39. 1 1
      logic/jwts.go
  40. 0 110
      logic/network.go
  41. 461 0
      logic/networks.go
  42. 263 2
      logic/nodes.go
  43. 19 29
      logic/server.go
  44. 22 0
      logic/users.go
  45. 32 22
      logic/util.go
  46. 5 6
      logic/wireguard.go
  47. 26 16
      main.go
  48. 0 32
      models/extclient.go
  49. 0 166
      models/network.go
  50. 0 172
      models/node.go
  51. 0 4
      netclient/functions/checkin.go
  52. 4 4
      netclient/functions/join.go
  53. 1 1
      netclient/main.go
  54. 9 2
      netclient/ncutils/netclientutils.go
  55. 2 0
      netclient/wireguard/common.go
  56. 0 38
      relay/relay.go
  57. 80 26
      scripts/netclient-install.sh
  58. 109 0
      scripts/nm-quick-server.sh
  59. 81 13
      scripts/nm-quick.sh
  60. 87 1
      servercfg/serverconf.go
  61. 1 1
      servercfg/sqlconf.go
  62. 5 4
      serverctl/serverctl.go
  63. 1 1
      validation/validation.go

+ 2 - 1
.github/dependabot.yml

@@ -7,4 +7,5 @@ updates:
     directory: "/"
     # Check for updates every day (weekdays)
     schedule:
-      interval: "daily"
+      interval: "weekly"
+    target-branch: "develop"

+ 5 - 1
README.md

@@ -8,7 +8,7 @@
 
 <p align="center">
   <a href="https://github.com/gravitl/netmaker/releases">
-    <img src="https://img.shields.io/badge/Version-0.8.4-informational?style=flat-square" />
+    <img src="https://img.shields.io/badge/Version-0.8.5-informational?style=flat-square" />
   </a>
   <a href="https://discord.gg/zRb9Vfhk8A">
     <img src="https://img.shields.io/badge/community-discord-informational" />
@@ -44,6 +44,10 @@
 
 `sudo wget -qO - https://raw.githubusercontent.com/gravitl/netmaker/develop/scripts/nm-quick.sh | bash`
 
+Or you can use your own domain and/or email. Make sure you have setup your wildcard domain pointing to server ip.
+
+`sudo wget -qO - https://raw.githubusercontent.com/gravitl/netmaker/develop/scripts/nm-quick.sh | bash -s domain=mynetmaker.domain.com [email protected]`
+
 <img src="./docs/images/install-server.gif" width="50%" /><img src="./docs/images/visit-website.gif" width="50%" />
 
 After installing Netmaker, check out the [Walkthrough](https://itnext.io/getting-started-with-netmaker-a-wireguard-virtual-networking-platform-3d563fbd87f0) and [Getting Started](https://netmaker.readthedocs.io/en/master/getting-started.html) guide to begin setting up networks. Or, check out some of our other [Tutorials](https://gravitl.com/resources) for different use cases, including Kubernetes.

+ 157 - 0
auth/auth.go

@@ -0,0 +1,157 @@
+package auth
+
+import (
+	"encoding/base64"
+	"encoding/json"
+	"net/http"
+
+	"github.com/gravitl/netmaker/logic"
+	"github.com/gravitl/netmaker/models"
+	"github.com/gravitl/netmaker/servercfg"
+	"golang.org/x/crypto/bcrypt"
+	"golang.org/x/oauth2"
+)
+
+// == consts ==
+const (
+	init_provider          = "initprovider"
+	get_user_info          = "getuserinfo"
+	handle_callback        = "handlecallback"
+	handle_login           = "handlelogin"
+	google_provider_name   = "google"
+	azure_ad_provider_name = "azure-ad"
+	github_provider_name   = "github"
+	verify_user            = "verifyuser"
+	auth_key               = "netmaker_auth"
+)
+
+var oauth_state_string = "netmaker-oauth-state" // should be set randomly each provider login
+var auth_provider *oauth2.Config
+
+func getCurrentAuthFunctions() map[string]interface{} {
+	var authInfo = servercfg.GetAuthProviderInfo()
+	var authProvider = authInfo[0]
+	switch authProvider {
+	case google_provider_name:
+		return google_functions
+	case azure_ad_provider_name:
+		return azure_ad_functions
+	case github_provider_name:
+		return github_functions
+	default:
+		return nil
+	}
+}
+
+// InitializeAuthProvider - initializes the auth provider if any is present
+func InitializeAuthProvider() string {
+	var functions = getCurrentAuthFunctions()
+	if functions == nil {
+		return ""
+	}
+	var _, err = fetchPassValue(logic.RandomString(64))
+	if err != nil {
+		logic.Log(err.Error(), 0)
+		return ""
+	}
+	var currentFrontendURL = servercfg.GetFrontendURL()
+	if currentFrontendURL == "" {
+		return ""
+	}
+	var authInfo = servercfg.GetAuthProviderInfo()
+	functions[init_provider].(func(string, string, string))(servercfg.GetAPIConnString()+"/api/oauth/callback", authInfo[1], authInfo[2])
+	return authInfo[0]
+}
+
+// HandleAuthCallback - handles oauth callback
+func HandleAuthCallback(w http.ResponseWriter, r *http.Request) {
+	var functions = getCurrentAuthFunctions()
+	if functions == nil {
+		return
+	}
+	functions[handle_callback].(func(http.ResponseWriter, *http.Request))(w, r)
+}
+
+// HandleAuthLogin - handles oauth login
+func HandleAuthLogin(w http.ResponseWriter, r *http.Request) {
+	var functions = getCurrentAuthFunctions()
+	if functions == nil {
+		return
+	}
+	functions[handle_login].(func(http.ResponseWriter, *http.Request))(w, r)
+}
+
+// IsOauthUser - returns
+func IsOauthUser(user *models.User) error {
+	var currentValue, err = fetchPassValue("")
+	if err != nil {
+		return err
+	}
+	var bCryptErr = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(currentValue))
+	return bCryptErr
+}
+
+// == private methods ==
+
+func addUser(email string) error {
+	var hasAdmin, err = logic.HasAdmin()
+	if err != nil {
+		logic.Log("error checking for existence of admin user during OAuth login for "+email+", user not added", 1)
+		return err
+	} // generate random password to adapt to current model
+	var newPass, fetchErr = fetchPassValue("")
+	if fetchErr != nil {
+		return fetchErr
+	}
+	var newUser = models.User{
+		UserName: email,
+		Password: newPass,
+	}
+	if !hasAdmin { // must be first attempt, create an admin
+		if newUser, err = logic.CreateAdmin(newUser); err != nil {
+			logic.Log("error creating admin from user, "+email+", user not added", 1)
+		} else {
+			logic.Log("admin created from user, "+email+", was first user added", 0)
+		}
+	} else { // otherwise add to db as admin..?
+		// TODO: add ability to add users with preemptive permissions
+		newUser.IsAdmin = false
+		if newUser, err = logic.CreateUser(newUser); err != nil {
+			logic.Log("error creating user, "+email+", user not added", 1)
+		} else {
+			logic.Log("user created from, "+email+"", 0)
+		}
+	}
+	return nil
+}
+
+func fetchPassValue(newValue string) (string, error) {
+
+	type valueHolder struct {
+		Value string `json:"value" bson:"value"`
+	}
+	var b64NewValue = base64.StdEncoding.EncodeToString([]byte(newValue))
+	var newValueHolder = &valueHolder{
+		Value: b64NewValue,
+	}
+	var data, marshalErr = json.Marshal(newValueHolder)
+	if marshalErr != nil {
+		return "", marshalErr
+	}
+
+	var currentValue, err = logic.FetchAuthSecret(auth_key, string(data))
+	if err != nil {
+		return "", err
+	}
+	var unmarshErr = json.Unmarshal([]byte(currentValue), newValueHolder)
+	if unmarshErr != nil {
+		return "", unmarshErr
+	}
+
+	var b64CurrentValue, b64Err = base64.StdEncoding.DecodeString(newValueHolder.Value)
+	if b64Err != nil {
+		logic.Log("could not decode pass", 0)
+		return "", nil
+	}
+	return string(b64CurrentValue), nil
+}

+ 126 - 0
auth/azure-ad.go

@@ -0,0 +1,126 @@
+package auth
+
+import (
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	"net/http"
+	"os"
+
+	"github.com/gravitl/netmaker/logic"
+	"github.com/gravitl/netmaker/models"
+	"github.com/gravitl/netmaker/servercfg"
+	"golang.org/x/oauth2"
+	"golang.org/x/oauth2/microsoft"
+)
+
+var azure_ad_functions = map[string]interface{}{
+	init_provider:   initAzureAD,
+	get_user_info:   getAzureUserInfo,
+	handle_callback: handleAzureCallback,
+	handle_login:    handleAzureLogin,
+	verify_user:     verifyAzureUser,
+}
+
+type azureOauthUser struct {
+	UserPrincipalName string `json:"userPrincipalName" bson:"userPrincipalName"`
+	AccessToken       string `json:"accesstoken" bson:"accesstoken"`
+}
+
+// == handle azure ad authentication here ==
+
+func initAzureAD(redirectURL string, clientID string, clientSecret string) {
+	auth_provider = &oauth2.Config{
+		RedirectURL:  redirectURL,
+		ClientID:     clientID,
+		ClientSecret: clientSecret,
+		Scopes:       []string{"User.Read"},
+		Endpoint:     microsoft.AzureADEndpoint(os.Getenv("AZURE_TENANT")),
+	}
+}
+
+func handleAzureLogin(w http.ResponseWriter, r *http.Request) {
+	oauth_state_string = logic.RandomString(16)
+	if auth_provider == nil && servercfg.GetFrontendURL() != "" {
+		http.Redirect(w, r, servercfg.GetFrontendURL()+"?oauth=callback-error", http.StatusTemporaryRedirect)
+		return
+	} else if auth_provider == nil {
+		fmt.Fprintf(w, "%s", []byte("no frontend URL was provided and an OAuth login was attempted\nplease reconfigure server to use OAuth or use basic credentials"))
+		return
+	}
+	var url = auth_provider.AuthCodeURL(oauth_state_string)
+	http.Redirect(w, r, url, http.StatusTemporaryRedirect)
+}
+
+func handleAzureCallback(w http.ResponseWriter, r *http.Request) {
+
+	var content, err = getAzureUserInfo(r.FormValue("state"), r.FormValue("code"))
+	if err != nil {
+		logic.Log("error when getting user info from azure: "+err.Error(), 1)
+		http.Redirect(w, r, servercfg.GetFrontendURL()+"?oauth=callback-error", http.StatusTemporaryRedirect)
+		return
+	}
+	_, err = logic.GetUser(content.UserPrincipalName)
+	if err != nil { // user must not exists, so try to make one
+		if err = addUser(content.UserPrincipalName); err != nil {
+			return
+		}
+	}
+	var newPass, fetchErr = fetchPassValue("")
+	if fetchErr != nil {
+		return
+	}
+	// send a netmaker jwt token
+	var authRequest = models.UserAuthParams{
+		UserName: content.UserPrincipalName,
+		Password: newPass,
+	}
+
+	var jwt, jwtErr = logic.VerifyAuthRequest(authRequest)
+	if jwtErr != nil {
+		logic.Log("could not parse jwt for user "+authRequest.UserName, 1)
+		return
+	}
+
+	logic.Log("completed azure OAuth sigin in for "+content.UserPrincipalName, 1)
+	http.Redirect(w, r, servercfg.GetFrontendURL()+"?login="+jwt+"&user="+content.UserPrincipalName, http.StatusPermanentRedirect)
+}
+
+func getAzureUserInfo(state string, code string) (*azureOauthUser, error) {
+	if state != oauth_state_string {
+		return nil, fmt.Errorf("invalid oauth state")
+	}
+	var token, err = auth_provider.Exchange(oauth2.NoContext, code)
+	if err != nil {
+		return nil, fmt.Errorf("code exchange failed: %s", err.Error())
+	}
+	var data []byte
+	data, err = json.Marshal(token)
+	if err != nil {
+		return nil, fmt.Errorf("failed to convert token to json: %s", err.Error())
+	}
+	var httpReq, reqErr = http.NewRequest("GET", "https://graph.microsoft.com/v1.0/me", nil)
+	if reqErr != nil {
+		return nil, fmt.Errorf("failed to create request to GitHub")
+	}
+	httpReq.Header.Set("Authorization", "Bearer "+token.AccessToken)
+	response, err := http.DefaultClient.Do(httpReq)
+	if err != nil {
+		return nil, fmt.Errorf("failed getting user info: %s", err.Error())
+	}
+	defer response.Body.Close()
+	contents, err := ioutil.ReadAll(response.Body)
+	if err != nil {
+		return nil, fmt.Errorf("failed reading response body: %s", err.Error())
+	}
+	var userInfo = &azureOauthUser{}
+	if err = json.Unmarshal(contents, userInfo); err != nil {
+		return nil, fmt.Errorf("failed parsing email from response data: %s", err.Error())
+	}
+	userInfo.AccessToken = string(data)
+	return userInfo, nil
+}
+
+func verifyAzureUser(token *oauth2.Token) bool {
+	return token.Valid()
+}

+ 129 - 0
auth/github.go

@@ -0,0 +1,129 @@
+package auth
+
+import (
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	"net/http"
+
+	"github.com/gravitl/netmaker/logic"
+	"github.com/gravitl/netmaker/models"
+	"github.com/gravitl/netmaker/servercfg"
+	"golang.org/x/oauth2"
+	"golang.org/x/oauth2/github"
+)
+
+var github_functions = map[string]interface{}{
+	init_provider:   initGithub,
+	get_user_info:   getGithubUserInfo,
+	handle_callback: handleGithubCallback,
+	handle_login:    handleGithubLogin,
+	verify_user:     verifyGithubUser,
+}
+
+type githubOauthUser struct {
+	Login       string `json:"login" bson:"login"`
+	AccessToken string `json:"accesstoken" bson:"accesstoken"`
+}
+
+// == handle github authentication here ==
+
+func initGithub(redirectURL string, clientID string, clientSecret string) {
+	auth_provider = &oauth2.Config{
+		RedirectURL:  redirectURL,
+		ClientID:     clientID,
+		ClientSecret: clientSecret,
+		Scopes:       []string{},
+		Endpoint:     github.Endpoint,
+	}
+}
+
+func handleGithubLogin(w http.ResponseWriter, r *http.Request) {
+	oauth_state_string = logic.RandomString(16)
+	if auth_provider == nil && servercfg.GetFrontendURL() != "" {
+		http.Redirect(w, r, servercfg.GetFrontendURL()+"?error=callback-error", http.StatusTemporaryRedirect)
+		return
+	} else if auth_provider == nil {
+		fmt.Fprintf(w, "%s", []byte("no frontend URL was provided and an OAuth login was attempted\nplease reconfigure server to use OAuth or use basic credentials"))
+		return
+	}
+	var url = auth_provider.AuthCodeURL(oauth_state_string)
+	http.Redirect(w, r, url, http.StatusTemporaryRedirect)
+}
+
+func handleGithubCallback(w http.ResponseWriter, r *http.Request) {
+
+	var content, err = getGithubUserInfo(r.URL.Query().Get("state"), r.URL.Query().Get("code"))
+	if err != nil {
+		logic.Log("error when getting user info from github: "+err.Error(), 1)
+		http.Redirect(w, r, servercfg.GetFrontendURL()+"?oauth=callback-error", http.StatusTemporaryRedirect)
+		return
+	}
+	_, err = logic.GetUser(content.Login)
+	if err != nil { // user must not exist, so try to make one
+		if err = addUser(content.Login); err != nil {
+			return
+		}
+	}
+	var newPass, fetchErr = fetchPassValue("")
+	if fetchErr != nil {
+		return
+	}
+	// send a netmaker jwt token
+	var authRequest = models.UserAuthParams{
+		UserName: content.Login,
+		Password: newPass,
+	}
+
+	var jwt, jwtErr = logic.VerifyAuthRequest(authRequest)
+	if jwtErr != nil {
+		logic.Log("could not parse jwt for user "+authRequest.UserName, 1)
+		return
+	}
+
+	logic.Log("completed github OAuth sigin in for "+content.Login, 1)
+	http.Redirect(w, r, servercfg.GetFrontendURL()+"?login="+jwt+"&user="+content.Login, http.StatusPermanentRedirect)
+}
+
+func getGithubUserInfo(state string, code string) (*githubOauthUser, error) {
+	if state != oauth_state_string {
+		return nil, fmt.Errorf("invalid OAuth state")
+	}
+	var token, err = auth_provider.Exchange(oauth2.NoContext, code)
+	if err != nil {
+		return nil, fmt.Errorf("code exchange failed: %s", err.Error())
+	}
+	if !token.Valid() {
+		return nil, fmt.Errorf("GitHub code exchange yielded invalid token")
+	}
+	var data []byte
+	data, err = json.Marshal(token)
+	if err != nil {
+		return nil, fmt.Errorf("failed to convert token to json: %s", err.Error())
+	}
+	var httpClient = &http.Client{}
+	var httpReq, reqErr = http.NewRequest("GET", "https://api.github.com/user", nil)
+	if reqErr != nil {
+		return nil, fmt.Errorf("failed to create request to GitHub")
+	}
+	httpReq.Header.Set("Authorization", "token "+token.AccessToken)
+	response, err := httpClient.Do(httpReq)
+	if err != nil {
+		return nil, fmt.Errorf("failed getting user info: %s", err.Error())
+	}
+	defer response.Body.Close()
+	contents, err := ioutil.ReadAll(response.Body)
+	if err != nil {
+		return nil, fmt.Errorf("failed reading response body: %s", err.Error())
+	}
+	var userInfo = &githubOauthUser{}
+	if err = json.Unmarshal(contents, userInfo); err != nil {
+		return nil, fmt.Errorf("failed parsing email from response data: %s", err.Error())
+	}
+	userInfo.AccessToken = string(data)
+	return userInfo, nil
+}
+
+func verifyGithubUser(token *oauth2.Token) bool {
+	return token.Valid()
+}

+ 120 - 0
auth/google.go

@@ -0,0 +1,120 @@
+package auth
+
+import (
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	"net/http"
+
+	"github.com/gravitl/netmaker/logic"
+	"github.com/gravitl/netmaker/models"
+	"github.com/gravitl/netmaker/servercfg"
+	"golang.org/x/oauth2"
+	"golang.org/x/oauth2/google"
+)
+
+var google_functions = map[string]interface{}{
+	init_provider:   initGoogle,
+	get_user_info:   getGoogleUserInfo,
+	handle_callback: handleGoogleCallback,
+	handle_login:    handleGoogleLogin,
+	verify_user:     verifyGoogleUser,
+}
+
+type googleOauthUser struct {
+	Email       string `json:"email" bson:"email"`
+	AccessToken string `json:"accesstoken" bson:"accesstoken"`
+}
+
+// == handle google authentication here ==
+
+func initGoogle(redirectURL string, clientID string, clientSecret string) {
+	auth_provider = &oauth2.Config{
+		RedirectURL:  redirectURL,
+		ClientID:     clientID,
+		ClientSecret: clientSecret,
+		Scopes:       []string{"https://www.googleapis.com/auth/userinfo.email"},
+		Endpoint:     google.Endpoint,
+	}
+}
+
+func handleGoogleLogin(w http.ResponseWriter, r *http.Request) {
+	oauth_state_string = logic.RandomString(16)
+	if auth_provider == nil && servercfg.GetFrontendURL() != "" {
+		http.Redirect(w, r, servercfg.GetFrontendURL()+"?oauth=callback-error", http.StatusTemporaryRedirect)
+		return
+	} else if auth_provider == nil {
+		fmt.Fprintf(w, "%s", []byte("no frontend URL was provided and an OAuth login was attempted\nplease reconfigure server to use OAuth or use basic credentials"))
+		return
+	}
+	var url = auth_provider.AuthCodeURL(oauth_state_string)
+	http.Redirect(w, r, url, http.StatusTemporaryRedirect)
+}
+
+func handleGoogleCallback(w http.ResponseWriter, r *http.Request) {
+
+	var content, err = getGoogleUserInfo(r.FormValue("state"), r.FormValue("code"))
+	if err != nil {
+		logic.Log("error when getting user info from google: "+err.Error(), 1)
+		http.Redirect(w, r, servercfg.GetFrontendURL()+"?oauth=callback-error", http.StatusTemporaryRedirect)
+		return
+	}
+	_, err = logic.GetUser(content.Email)
+	if err != nil { // user must not exists, so try to make one
+		if err = addUser(content.Email); err != nil {
+			return
+		}
+	}
+	var newPass, fetchErr = fetchPassValue("")
+	if fetchErr != nil {
+		return
+	}
+	// send a netmaker jwt token
+	var authRequest = models.UserAuthParams{
+		UserName: content.Email,
+		Password: newPass,
+	}
+
+	var jwt, jwtErr = logic.VerifyAuthRequest(authRequest)
+	if jwtErr != nil {
+		logic.Log("could not parse jwt for user "+authRequest.UserName, 1)
+		return
+	}
+
+	logic.Log("completed google OAuth sigin in for "+content.Email, 1)
+	http.Redirect(w, r, servercfg.GetFrontendURL()+"?login="+jwt+"&user="+content.Email, http.StatusPermanentRedirect)
+}
+
+func getGoogleUserInfo(state string, code string) (*googleOauthUser, error) {
+	if state != oauth_state_string {
+		return nil, fmt.Errorf("invalid OAuth state")
+	}
+	var token, err = auth_provider.Exchange(oauth2.NoContext, code)
+	if err != nil {
+		return nil, fmt.Errorf("code exchange failed: %s", err.Error())
+	}
+	var data []byte
+	data, err = json.Marshal(token)
+	if err != nil {
+		return nil, fmt.Errorf("failed to convert token to json: %s", err.Error())
+	}
+	response, err := http.Get("https://www.googleapis.com/oauth2/v2/userinfo?access_token=" + token.AccessToken)
+	if err != nil {
+		return nil, fmt.Errorf("failed getting user info: %s", err.Error())
+	}
+	defer response.Body.Close()
+	contents, err := ioutil.ReadAll(response.Body)
+	if err != nil {
+		return nil, fmt.Errorf("failed reading response body: %s", err.Error())
+	}
+	var userInfo = &googleOauthUser{}
+	if err = json.Unmarshal(contents, userInfo); err != nil {
+		return nil, fmt.Errorf("failed parsing email from response data: %s", err.Error())
+	}
+	userInfo.AccessToken = string(data)
+	return userInfo, nil
+}
+
+func verifyGoogleUser(token *oauth2.Token) bool {
+	return token.Valid()
+}

+ 7 - 4
compose/docker-compose.caddy.yml

@@ -3,16 +3,15 @@ version: "3.4"
 services:
   netmaker:
     container_name: netmaker
-    image: gravitl/netmaker:v0.8.4
+    image: gravitl/netmaker:v0.8.5
     volumes:
-      - /etc/netclient/config:/etc/netclient/config
       - dnsconfig:/root/config/dnsconfig
       - /usr/bin/wg:/usr/bin/wg
       - sqldata:/root/data
     cap_add: 
       - NET_ADMIN
     restart: always
-    network_mode: host
+    privileged: true
     environment:
       SERVER_HOST: "SERVER_PUBLIC_IP"
       SERVER_API_CONN_STRING: "api.NETMAKER_BASE_DOMAIN:443"
@@ -29,11 +28,15 @@ services:
       SERVER_GRPC_WIREGUARD: "off"
       CORS_ALLOWED_ORIGIN: "*"
       DATABASE: "sqlite"
+    ports:
+      - "51821-51830:51821-51830/udp"
+      - "8081:8081"
+      - "50051:50051"
   netmaker-ui:
     container_name: netmaker-ui
     depends_on:
       - netmaker
-    image: gravitl/netmaker-ui:v0.8
+    image: gravitl/netmaker-ui:v0.8.5
     links:
       - "netmaker:api"
     ports:

+ 8 - 5
compose/docker-compose.nodns.yml

@@ -3,15 +3,14 @@ version: "3.4"
 services:
   netmaker:
     container_name: netmaker
-    image: gravitl/netmaker:v0.8.4
+    image: gravitl/netmaker:v0.8.5
     volumes:
-      - /etc/netclient/config:/etc/netclient/config
       - /usr/bin/wg:/usr/bin/wg
       - sqldata:/root/data
     cap_add: 
       - NET_ADMIN
     restart: always
-    network_mode: host
+    privileged: true
     environment:
       SERVER_HOST: "SERVER_PUBLIC_IP"
       SERVER_API_CONN_STRING: "api.NETMAKER_BASE_DOMAIN:443"
@@ -28,11 +27,15 @@ services:
       SERVER_GRPC_WIREGUARD: "off"
       CORS_ALLOWED_ORIGIN: "*"
       DATABASE: "sqlite"
+    ports:
+      - "51821-51830:51821-51830/udp"
+      - "8081:8081"
+      - "50051:50051"
   netmaker-ui:
     container_name: netmaker-ui
     depends_on:
       - netmaker
-    image: gravitl/netmaker-ui:v0.8
+    image: gravitl/netmaker-ui:v0.8.5
     links:
       - "netmaker:api"
     ports:
@@ -53,4 +56,4 @@ services:
 volumes:
   caddy_data: {}
   caddy_conf: {}
-  sqldata: {}
+  sqldata: {}

+ 3 - 9
compose/docker-compose.reference.yml

@@ -11,19 +11,12 @@ services:
     container_name: netmaker
     depends_on:
       - rqlite
-    image: gravitl/netmaker:v0.7
-    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
+    image: gravitl/netmaker:v0.8.4
+    volumes: # Volume mounts necessary for CLIENT_MODE to control wireguard networking on host (except dnsconfig, which is where dns config files are stored for use by CoreDNS)
       - dnsconfig:/root/config/dnsconfig # Netmaker writes Corefile to this location, which gets mounted by CoreDNS for DNS configuration.
       - /usr/bin/wg:/usr/bin/wg
-      - /var/run/dbus/system_bus_socket:/var/run/dbus/system_bus_socket
-      - /run/systemd/system:/run/systemd/system
-      - /etc/systemd/system:/etc/systemd/system
-      - /sys/fs/cgroup:/sys/fs/cgroup
     cap_add: # Necessary for CLIENT_MODE. Should be removed if turned off. 
       - NET_ADMIN
-      - SYS_MODULE
     restart: always
     network_mode: host # Necessary for CLIENT_MODE. Should be removed if turned off, but then need to add port mappings
     environment:
@@ -32,6 +25,7 @@ services:
       SERVER_GRPC_HOST: "127.0.0.1" # Overrides SERVER_HOST if set. Useful for making HTTP and GRPC available via different interfaces/networks.
       API_PORT: 8081 # The HTTP API port for Netmaker. Used for API calls / communication from front end. If changed, need to change port of BACKEND_URL for netmaker-ui.
       GRPC_PORT: 50051 # The GRPC port for Netmaker. Used for communications from nodes.
+      CLIENT_MODE: "on" # on if netmaker should run its own client, off if not.
       MASTER_KEY: "secretkey" # The admin master key for accessing the API. Change this in any production installation.
       CORS_ALLOWED_ORIGIN: "*" # The "allowed origin" for API requests. Change to restrict where API requests can come from.
       REST_BACKEND: "on" # Enables the REST backend (API running on API_PORT at SERVER_HTTP_HOST). Change to "off" to turn off.

+ 7 - 4
compose/docker-compose.yml

@@ -3,16 +3,15 @@ version: "3.4"
 services:
   netmaker:
     container_name: netmaker
-    image: gravitl/netmaker:v0.8.4
+    image: gravitl/netmaker:v0.8.5
     volumes:
-      - /etc/netclient/config:/etc/netclient/config
       - dnsconfig:/root/config/dnsconfig
       - /usr/bin/wg:/usr/bin/wg
       - sqldata:/root/data
     cap_add: 
       - NET_ADMIN
     restart: always
-    network_mode: host
+    privileged: true
     environment:
       SERVER_HOST: "SERVER_PUBLIC_IP"
       SERVER_API_CONN_STRING: "api.NETMAKER_BASE_DOMAIN:443"
@@ -29,11 +28,15 @@ services:
       SERVER_GRPC_WIREGUARD: "off"
       CORS_ALLOWED_ORIGIN: "*"
       DATABASE: "sqlite"
+    ports:
+      - "51821-51830:51821-51830/udp"
+      - "8081:8081"
+      - "50051:50051"
   netmaker-ui:
     container_name: netmaker-ui
     depends_on:
       - netmaker
-    image: gravitl/netmaker-ui:v0.8
+    image: gravitl/netmaker-ui:v0.8.5
     links:
       - "netmaker:api"
     ports:

+ 35 - 32
config/config.go

@@ -30,49 +30,52 @@ var Config *EnvironmentConfig
 // EnvironmentConfig :
 type EnvironmentConfig struct {
 	Server ServerConfig `yaml:"server"`
-	SQL SQLConfig `yaml:"sql"`
+	SQL    SQLConfig    `yaml:"sql"`
 }
 
 // ServerConfig :
 type ServerConfig struct {
-	CoreDNSAddr          string `yaml:"corednsaddr"`
-	APIConnString        string `yaml:"apiconn"`
-	APIHost              string `yaml:"apihost"`
-	APIPort              string `yaml:"apiport"`
-	GRPCConnString       string `yaml:"grpcconn"`
-	GRPCHost             string `yaml:"grpchost"`
-	GRPCPort             string `yaml:"grpcport"`
-	GRPCSecure           string `yaml:"grpcsecure"`
-	MasterKey            string `yaml:"masterkey"`
-	AllowedOrigin        string `yaml:"allowedorigin"`
-	NodeID        string `yaml:"nodeid"`
-	RestBackend          string `yaml:"restbackend"`
-	AgentBackend         string `yaml:"agentbackend"`
-	ClientMode           string `yaml:"clientmode"`
-	DNSMode              string `yaml:"dnsmode"`
-	SplitDNS             string `yaml:"splitdns"`
-	DisableRemoteIPCheck string `yaml:"disableremoteipcheck"`
-	DisableDefaultNet    string `yaml:"disabledefaultnet"`
-	GRPCSSL              string `yaml:"grpcssl"`
-	Version              string `yaml:"version"`
-	SQLConn              string `yaml:"sqlconn"`
-	Platform             string `yaml:"platform"`
-	Database             string `yaml:database`
-	CheckinInterval      string `yaml:checkininterval`
-	DefaultNodeLimit     int32  `yaml:"defaultnodelimit"`
-	Verbosity            int32  `yaml:"verbosity"`
+	CoreDNSAddr           string `yaml:"corednsaddr"`
+	APIConnString         string `yaml:"apiconn"`
+	APIHost               string `yaml:"apihost"`
+	APIPort               string `yaml:"apiport"`
+	GRPCConnString        string `yaml:"grpcconn"`
+	GRPCHost              string `yaml:"grpchost"`
+	GRPCPort              string `yaml:"grpcport"`
+	GRPCSecure            string `yaml:"grpcsecure"`
+	MasterKey             string `yaml:"masterkey"`
+	AllowedOrigin         string `yaml:"allowedorigin"`
+	NodeID                string `yaml:"nodeid"`
+	RestBackend           string `yaml:"restbackend"`
+	AgentBackend          string `yaml:"agentbackend"`
+	ClientMode            string `yaml:"clientmode"`
+	DNSMode               string `yaml:"dnsmode"`
+	SplitDNS              string `yaml:"splitdns"`
+	DisableRemoteIPCheck  string `yaml:"disableremoteipcheck"`
+	DisableDefaultNet     string `yaml:"disabledefaultnet"`
+	GRPCSSL               string `yaml:"grpcssl"`
+	Version               string `yaml:"version"`
+	SQLConn               string `yaml:"sqlconn"`
+	Platform              string `yaml:"platform"`
+	Database              string `yaml:database`
+	CheckinInterval       string `yaml:checkininterval`
+	DefaultNodeLimit      int32  `yaml:"defaultnodelimit"`
+	Verbosity             int32  `yaml:"verbosity"`
 	ServerCheckinInterval int64  `yaml:"servercheckininterval"`
+	AuthProvider          string `yaml:"authprovider"`
+	ClientID              string `yaml:"clientid"`
+	ClientSecret          string `yaml:"clientsecret"`
+	FrontendURL           string `yaml:"frontendurl"`
 }
 
-
 // Generic SQL Config
 type SQLConfig struct {
-	Host string `yaml:"host"`
-	Port int32 `yaml:"port"`
+	Host     string `yaml:"host"`
+	Port     int32  `yaml:"port"`
 	Username string `yaml:"username"`
 	Password string `yaml:"password"`
-	DB string `yaml:"db"`
-	SSLMode string `yaml:"sslmode"`
+	DB       string `yaml:"db"`
+	SSLMode  string `yaml:"sslmode"`
 }
 
 //reading in the env file

+ 5 - 4
controllers/authGrpc.go

@@ -8,6 +8,7 @@ import (
 	"github.com/gravitl/netmaker/database"
 	"github.com/gravitl/netmaker/functions"
 	nodepb "github.com/gravitl/netmaker/grpc"
+	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/models"
 	"golang.org/x/crypto/bcrypt"
 	"google.golang.org/grpc"
@@ -68,7 +69,7 @@ func grpcAuthorize(ctx context.Context) error {
 
 	authToken := authHeader[0]
 
-	mac, network, err := functions.VerifyToken(authToken)
+	mac, network, err := logic.VerifyToken(authToken)
 	if err != nil {
 		return err
 	}
@@ -79,9 +80,9 @@ func grpcAuthorize(ctx context.Context) error {
 		return status.Errorf(codes.Unauthenticated, "Unauthorized. Network does not exist: "+network)
 	}
 	emptynode := models.Node{}
-	node, err := functions.GetNodeByMacAddress(network, mac)
+	node, err := logic.GetNodeByMacAddress(network, mac)
 	if database.IsEmptyRecord(err) {
-		if node, err = functions.GetDeletedNodeByMacAddress(network, mac); err == nil {
+		if node, err = logic.GetDeletedNodeByMacAddress(network, mac); err == nil {
 			if functions.RemoveDeletedNode(node.ID) {
 				return status.Errorf(codes.Unauthenticated, models.NODE_DELETE)
 			}
@@ -146,7 +147,7 @@ func (s *NodeServiceServer) Login(ctx context.Context, req *nodepb.Object) (*nod
 			return nil, err
 		} else {
 			//Create a new JWT for the node
-			tokenString, err := functions.CreateJWT(macaddress, result.Network)
+			tokenString, err := logic.CreateJWT(macaddress, result.Network)
 
 			if err != nil {
 				return nil, err

+ 4 - 4
controllers/common.go

@@ -5,8 +5,8 @@ import (
 	"strings"
 
 	"github.com/gravitl/netmaker/database"
-	"github.com/gravitl/netmaker/dnslogic"
 	"github.com/gravitl/netmaker/functions"
+	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/servercfg"
 )
@@ -41,7 +41,7 @@ func DeleteNode(key string, exterminate bool) error {
 		return err
 	}
 	if servercfg.IsDNSMode() {
-		err = dnslogic.SetDNS()
+		err = logic.SetDNS()
 	}
 	return err
 }
@@ -60,7 +60,7 @@ func GetNode(macaddress string, network string) (models.Node, error) {
 
 	var node models.Node
 
-	key, err := functions.GetRecordKey(macaddress, network)
+	key, err := logic.GetRecordKey(macaddress, network)
 	if err != nil {
 		return node, err
 	}
@@ -75,7 +75,7 @@ func GetNode(macaddress string, network string) (models.Node, error) {
 	if err = json.Unmarshal([]byte(data), &node); err != nil {
 		return node, err
 	}
-	node.SetDefaults()
+	logic.SetNodeDefaults(&node)
 
 	return node, err
 }

+ 4 - 4
controllers/controller.go

@@ -10,6 +10,7 @@ import (
 
 	"github.com/gorilla/handlers"
 	"github.com/gorilla/mux"
+	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/servercfg"
 )
 
@@ -42,8 +43,7 @@ func HandleRESTRequests(wg *sync.WaitGroup) {
 			log.Println(err)
 		}
 	}()
-
-	log.Println("REST Server successfully started on port " + port + " (REST)")
+	logic.Log("REST Server successfully started on port "+port+" (REST)", 0)
 	c := make(chan os.Signal)
 
 	// Relay os.Interrupt to our channel (os.Interrupt = CTRL+C)
@@ -55,7 +55,7 @@ func HandleRESTRequests(wg *sync.WaitGroup) {
 	<-c
 
 	// After receiving CTRL+C Properly stop the server
-	log.Println("Stopping the REST server...")
+	logic.Log("Stopping the REST server...", 0)
 	srv.Shutdown(context.TODO())
-	log.Println("REST Server closed.")
+	logic.Log("REST Server closed.", 0)
 }

+ 17 - 17
controllers/dnsHttpController.go

@@ -7,8 +7,8 @@ import (
 	"github.com/go-playground/validator/v10"
 	"github.com/gorilla/mux"
 	"github.com/gravitl/netmaker/database"
-	"github.com/gravitl/netmaker/dnslogic"
 	"github.com/gravitl/netmaker/functions"
+	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/models"
 )
 
@@ -59,12 +59,12 @@ func getAllDNS(w http.ResponseWriter, r *http.Request) {
 // GetAllDNS - gets all dns entries
 func GetAllDNS() ([]models.DNSEntry, error) {
 	var dns []models.DNSEntry
-	networks, err := models.GetNetworks()
+	networks, err := logic.GetNetworks()
 	if err != nil && !database.IsEmptyRecord(err) {
 		return []models.DNSEntry{}, err
 	}
 	for _, net := range networks {
-		netdns, err := dnslogic.GetDNS(net.NetID)
+		netdns, err := logic.GetDNS(net.NetID)
 		if err != nil {
 			return []models.DNSEntry{}, nil
 		}
@@ -105,7 +105,7 @@ func getCustomDNS(w http.ResponseWriter, r *http.Request) {
 	var dns []models.DNSEntry
 	var params = mux.Vars(r)
 
-	dns, err := dnslogic.GetCustomDNS(params["network"])
+	dns, err := logic.GetCustomDNS(params["network"])
 	if err != nil {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
@@ -121,7 +121,7 @@ func GetDNSEntryNum(domain string, network string) (int, error) {
 
 	num := 0
 
-	entries, err := dnslogic.GetDNS(network)
+	entries, err := logic.GetDNS(network)
 	if err != nil {
 		return 0, err
 	}
@@ -144,7 +144,7 @@ func getDNS(w http.ResponseWriter, r *http.Request) {
 	var dns []models.DNSEntry
 	var params = mux.Vars(r)
 
-	dns, err := dnslogic.GetDNS(params["network"])
+	dns, err := logic.GetDNS(params["network"])
 	if err != nil {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
@@ -174,7 +174,7 @@ func createDNS(w http.ResponseWriter, r *http.Request) {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
 	}
-	err = dnslogic.SetDNS()
+	err = logic.SetDNS()
 	if err != nil {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
@@ -229,7 +229,7 @@ func updateDNS(w http.ResponseWriter, r *http.Request) {
 		returnErrorResponse(w, r, formatError(err, "badrequest"))
 		return
 	}
-	err = dnslogic.SetDNS()
+	err = logic.SetDNS()
 	if err != nil {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
@@ -252,7 +252,7 @@ func deleteDNS(w http.ResponseWriter, r *http.Request) {
 	}
 	entrytext := params["domain"] + "." + params["network"]
 	functions.PrintUserLog(models.NODE_SERVER_NAME, "deleted dns entry: "+entrytext, 1)
-	err = dnslogic.SetDNS()
+	err = logic.SetDNS()
 	if err != nil {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
@@ -267,7 +267,7 @@ func CreateDNS(entry models.DNSEntry) (models.DNSEntry, error) {
 	if err != nil {
 		return models.DNSEntry{}, err
 	}
-	key, err := functions.GetRecordKey(entry.Name, entry.Network)
+	key, err := logic.GetRecordKey(entry.Name, entry.Network)
 	if err != nil {
 		return models.DNSEntry{}, err
 	}
@@ -279,7 +279,7 @@ func CreateDNS(entry models.DNSEntry) (models.DNSEntry, error) {
 // GetDNSEntry - gets a DNS entry
 func GetDNSEntry(domain string, network string) (models.DNSEntry, error) {
 	var entry models.DNSEntry
-	key, err := functions.GetRecordKey(domain, network)
+	key, err := logic.GetRecordKey(domain, network)
 	if err != nil {
 		return entry, err
 	}
@@ -294,7 +294,7 @@ func GetDNSEntry(domain string, network string) (models.DNSEntry, error) {
 // UpdateDNS - updates DNS entry
 func UpdateDNS(dnschange models.DNSEntry, entry models.DNSEntry) (models.DNSEntry, error) {
 
-	key, err := functions.GetRecordKey(entry.Name, entry.Network)
+	key, err := logic.GetRecordKey(entry.Name, entry.Network)
 	if err != nil {
 		return entry, err
 	}
@@ -304,7 +304,7 @@ func UpdateDNS(dnschange models.DNSEntry, entry models.DNSEntry) (models.DNSEntr
 	if dnschange.Address != "" {
 		entry.Address = dnschange.Address
 	}
-	newkey, err := functions.GetRecordKey(entry.Name, entry.Network)
+	newkey, err := logic.GetRecordKey(entry.Name, entry.Network)
 
 	err = database.DeleteRecord(database.DNS_TABLE_NAME, key)
 	if err != nil {
@@ -318,7 +318,7 @@ func UpdateDNS(dnschange models.DNSEntry, entry models.DNSEntry) (models.DNSEntr
 
 // DeleteDNS - deletes a DNS entry
 func DeleteDNS(domain string, network string) error {
-	key, err := functions.GetRecordKey(domain, network)
+	key, err := logic.GetRecordKey(domain, network)
 	if err != nil {
 		return err
 	}
@@ -330,7 +330,7 @@ func pushDNS(w http.ResponseWriter, r *http.Request) {
 	// Set header
 	w.Header().Set("Content-Type", "application/json")
 
-	err := dnslogic.SetDNS()
+	err := logic.SetDNS()
 
 	if err != nil {
 		returnErrorResponse(w, r, formatError(err, "internal"))
@@ -351,7 +351,7 @@ func ValidateDNSCreate(entry models.DNSEntry) error {
 	})
 
 	_ = v.RegisterValidation("network_exists", func(fl validator.FieldLevel) bool {
-		_, err := functions.GetParentNetwork(entry.Network)
+		_, err := logic.GetParentNetwork(entry.Network)
 		return err == nil
 	})
 
@@ -378,7 +378,7 @@ func ValidateDNSUpdate(change models.DNSEntry, entry models.DNSEntry) error {
 		return err == nil && num == 0
 	})
 	_ = v.RegisterValidation("network_exists", func(fl validator.FieldLevel) bool {
-		_, err := functions.GetParentNetwork(change.Network)
+		_, err := logic.GetParentNetwork(change.Network)
 		if err != nil {
 			functions.PrintUserLog("", err.Error(), 0)
 		}

+ 13 - 14
controllers/dnsHttpController_test.go

@@ -6,7 +6,6 @@ import (
 	"testing"
 
 	"github.com/gravitl/netmaker/database"
-	"github.com/gravitl/netmaker/dnslogic"
 	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/models"
 	"github.com/stretchr/testify/assert"
@@ -68,33 +67,33 @@ func TestGetCustomDNS(t *testing.T) {
 	deleteAllDNS(t)
 	deleteAllNetworks()
 	t.Run("NoNetworks", func(t *testing.T) {
-		dns, err := dnslogic.GetCustomDNS("skynet")
+		dns, err := logic.GetCustomDNS("skynet")
 		assert.EqualError(t, err, "could not find any records")
 		assert.Equal(t, []models.DNSEntry(nil), dns)
 	})
 	t.Run("NoNodes", func(t *testing.T) {
 		createNet()
-		dns, err := dnslogic.GetCustomDNS("skynet")
+		dns, err := logic.GetCustomDNS("skynet")
 		assert.EqualError(t, err, "could not find any records")
 		assert.Equal(t, []models.DNSEntry(nil), dns)
 	})
 	t.Run("NodeExists", func(t *testing.T) {
 		createTestNode()
-		dns, err := dnslogic.GetCustomDNS("skynet")
+		dns, err := logic.GetCustomDNS("skynet")
 		assert.EqualError(t, err, "could not find any records")
 		assert.Equal(t, 0, len(dns))
 	})
 	t.Run("EntryExist", func(t *testing.T) {
 		entry := models.DNSEntry{"10.0.0.3", "newhost", "skynet"}
 		CreateDNS(entry)
-		dns, err := dnslogic.GetCustomDNS("skynet")
+		dns, err := logic.GetCustomDNS("skynet")
 		assert.Nil(t, err)
 		assert.Equal(t, 1, len(dns))
 	})
 	t.Run("MultipleEntries", func(t *testing.T) {
 		entry := models.DNSEntry{"10.0.0.4", "host4", "skynet"}
 		CreateDNS(entry)
-		dns, err := dnslogic.GetCustomDNS("skynet")
+		dns, err := logic.GetCustomDNS("skynet")
 		assert.Nil(t, err)
 		assert.Equal(t, 2, len(dns))
 	})
@@ -125,7 +124,7 @@ func TestGetDNS(t *testing.T) {
 	deleteAllNetworks()
 	createNet()
 	t.Run("NoEntries", func(t *testing.T) {
-		dns, err := dnslogic.GetDNS("skynet")
+		dns, err := logic.GetDNS("skynet")
 		assert.Nil(t, err)
 		assert.Nil(t, dns)
 	})
@@ -133,7 +132,7 @@ func TestGetDNS(t *testing.T) {
 		entry := models.DNSEntry{"10.0.0.2", "newhost", "skynet"}
 		_, err := CreateDNS(entry)
 		assert.Nil(t, err)
-		dns, err := dnslogic.GetDNS("skynet")
+		dns, err := logic.GetDNS("skynet")
 		t.Log(dns)
 		assert.Nil(t, err)
 		assert.NotNil(t, dns)
@@ -143,7 +142,7 @@ func TestGetDNS(t *testing.T) {
 	t.Run("NodeExists", func(t *testing.T) {
 		deleteAllDNS(t)
 		createTestNode()
-		dns, err := dnslogic.GetDNS("skynet")
+		dns, err := logic.GetDNS("skynet")
 		assert.Nil(t, err)
 		assert.NotNil(t, dns)
 		assert.Equal(t, "skynet", dns[0].Network)
@@ -152,7 +151,7 @@ func TestGetDNS(t *testing.T) {
 	t.Run("NodeAndCustomDNS", func(t *testing.T) {
 		entry := models.DNSEntry{"10.0.0.2", "newhost", "skynet"}
 		_, err := CreateDNS(entry)
-		dns, err := dnslogic.GetDNS("skynet")
+		dns, err := logic.GetDNS("skynet")
 		t.Log(dns)
 		assert.Nil(t, err)
 		assert.NotNil(t, dns)
@@ -178,7 +177,7 @@ func TestSetDNS(t *testing.T) {
 	deleteAllDNS(t)
 	deleteAllNetworks()
 	t.Run("NoNetworks", func(t *testing.T) {
-		err := dnslogic.SetDNS()
+		err := logic.SetDNS()
 		assert.Nil(t, err)
 		info, err := os.Stat("./config/dnsconfig/netmaker.hosts")
 		assert.Nil(t, err)
@@ -187,7 +186,7 @@ func TestSetDNS(t *testing.T) {
 	})
 	t.Run("NoEntries", func(t *testing.T) {
 		createNet()
-		err := dnslogic.SetDNS()
+		err := logic.SetDNS()
 		assert.Nil(t, err)
 		info, err := os.Stat("./config/dnsconfig/netmaker.hosts")
 		assert.Nil(t, err)
@@ -196,7 +195,7 @@ func TestSetDNS(t *testing.T) {
 	})
 	t.Run("NodeExists", func(t *testing.T) {
 		createTestNode()
-		err := dnslogic.SetDNS()
+		err := logic.SetDNS()
 		assert.Nil(t, err)
 		info, err := os.Stat("./config/dnsconfig/netmaker.hosts")
 		assert.Nil(t, err)
@@ -208,7 +207,7 @@ func TestSetDNS(t *testing.T) {
 	t.Run("EntryExists", func(t *testing.T) {
 		entry := models.DNSEntry{"10.0.0.3", "newhost", "skynet"}
 		CreateDNS(entry)
-		err := dnslogic.SetDNS()
+		err := logic.SetDNS()
 		assert.Nil(t, err)
 		info, err := os.Stat("./config/dnsconfig/netmaker.hosts")
 		assert.Nil(t, err)

+ 10 - 25
controllers/extClientHttpController.go

@@ -5,7 +5,6 @@ import (
 	"errors"
 	"fmt"
 	"io"
-	"math/rand"
 	"net/http"
 	"strconv"
 	"time"
@@ -31,7 +30,7 @@ func extClientHandlers(r *mux.Router) {
 }
 
 func checkIngressExists(network string, macaddress string) bool {
-	node, err := functions.GetNodeByMacAddress(network, macaddress)
+	node, err := logic.GetNodeByMacAddress(network, macaddress)
 	if err != nil {
 		return false
 	}
@@ -134,7 +133,7 @@ func getExtClient(w http.ResponseWriter, r *http.Request) {
 // GetExtClient - gets a single ext client on a network
 func GetExtClient(clientid string, network string) (models.ExtClient, error) {
 	var extclient models.ExtClient
-	key, err := functions.GetRecordKey(clientid, network)
+	key, err := logic.GetRecordKey(clientid, network)
 	if err != nil {
 		return extclient, err
 	}
@@ -161,14 +160,14 @@ func getExtClientConf(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
-	gwnode, err := functions.GetNodeByMacAddress(client.Network, client.IngressGatewayID)
+	gwnode, err := logic.GetNodeByMacAddress(client.Network, client.IngressGatewayID)
 	if err != nil {
 		functions.PrintUserLog(r.Header.Get("user"), "Could not retrieve Ingress Gateway Node "+client.IngressGatewayID, 1)
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
 	}
 
-	network, err := functions.GetParentNetwork(client.Network)
+	network, err := logic.GetParentNetwork(client.Network)
 	if err != nil {
 		functions.PrintUserLog(r.Header.Get("user"), "Could not retrieve Ingress Gateway Network "+client.Network, 1)
 		returnErrorResponse(w, r, formatError(err, "internal"))
@@ -180,7 +179,7 @@ func getExtClientConf(w http.ResponseWriter, r *http.Request) {
 	}
 	gwendpoint := gwnode.Endpoint + ":" + strconv.Itoa(int(gwnode.ListenPort))
 	newAllowedIPs := network.AddressRange
-	if egressGatewayRanges, err := client.GetEgressRangesOnNetwork(); err == nil {
+	if egressGatewayRanges, err := logic.GetEgressRangesOnNetwork(&client); err == nil {
 		for _, egressGatewayRange := range egressGatewayRanges {
 			newAllowedIPs += "," + egressGatewayRange
 		}
@@ -253,7 +252,7 @@ func CreateExtClient(extclient models.ExtClient) error {
 	}
 
 	if extclient.Address == "" {
-		newAddress, err := functions.UniqueAddress(extclient.Network)
+		newAddress, err := logic.UniqueAddress(extclient.Network)
 		if err != nil {
 			return err
 		}
@@ -266,7 +265,7 @@ func CreateExtClient(extclient models.ExtClient) error {
 
 	extclient.LastModified = time.Now().Unix()
 
-	key, err := functions.GetRecordKey(extclient.ClientID, extclient.Network)
+	key, err := logic.GetRecordKey(extclient.ClientID, extclient.Network)
 	if err != nil {
 		return err
 	}
@@ -301,7 +300,7 @@ func createExtClient(w http.ResponseWriter, r *http.Request) {
 	var extclient models.ExtClient
 	extclient.Network = networkName
 	extclient.IngressGatewayID = macaddress
-	node, err := functions.GetNodeByMacAddress(networkName, macaddress)
+	node, err := logic.GetNodeByMacAddress(networkName, macaddress)
 	if err != nil {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
@@ -330,7 +329,7 @@ func updateExtClient(w http.ResponseWriter, r *http.Request) {
 	var oldExtClient models.ExtClient
 	_ = json.NewDecoder(r.Body).Decode(&newExtClient)
 
-	key, err := functions.GetRecordKey(params["clientid"], params["network"])
+	key, err := logic.GetRecordKey(params["clientid"], params["network"])
 	if err != nil {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
@@ -368,7 +367,7 @@ func UpdateExtClient(newclientid string, network string, client models.ExtClient
 
 // DeleteExtClient - deletes an existing ext client
 func DeleteExtClient(network string, clientid string) error {
-	key, err := functions.GetRecordKey(clientid, network)
+	key, err := logic.GetRecordKey(clientid, network)
 	if err != nil {
 		return err
 	}
@@ -413,17 +412,3 @@ func deleteExtClient(w http.ResponseWriter, r *http.Request) {
 		"Deleted extclient client "+params["clientid"]+" from network "+params["network"], 1)
 	returnSuccessResponse(w, r, params["clientid"]+" deleted.")
 }
-
-// StringWithCharset - returns a random string in a charset
-func StringWithCharset(length int, charset string) string {
-	b := make([]byte, length)
-	for i := range b {
-		b[i] = charset[seededRand.Intn(len(charset))]
-	}
-	return string(b)
-}
-
-const charset = "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
-
-var seededRand *rand.Rand = rand.New(
-	rand.NewSource(time.Now().UnixNano()))

+ 12 - 12
controllers/networkHttpController.go

@@ -83,7 +83,7 @@ func SecurityCheck(reqAdmin bool, netname string, token string) (error, []string
 	isMasterAuthenticated := authenticateMaster(authToken)
 	username := ""
 	if !hasBearer || !isMasterAuthenticated {
-		userName, networks, isadmin, err := functions.VerifyUserToken(authToken)
+		userName, networks, isadmin, err := logic.VerifyUserToken(authToken)
 		username = userName
 		if err != nil {
 			return errors.New("error verifying user token"), nil, username
@@ -133,14 +133,14 @@ func getNetworks(w http.ResponseWriter, r *http.Request) {
 	allnetworks := []models.Network{}
 	err := errors.New("Networks Error")
 	if networksSlice[0] == ALL_NETWORK_ACCESS {
-		allnetworks, err = models.GetNetworks()
+		allnetworks, err = logic.GetNetworks()
 		if err != nil && !database.IsEmptyRecord(err) {
 			returnErrorResponse(w, r, formatError(err, "internal"))
 			return
 		}
 	} else {
 		for _, network := range networksSlice {
-			netObject, parentErr := functions.GetParentNetwork(network)
+			netObject, parentErr := logic.GetParentNetwork(network)
 			if parentErr == nil {
 				allnetworks = append(allnetworks, netObject)
 			}
@@ -228,7 +228,7 @@ func KeyUpdate(netname string) (models.Network, error) {
 func AlertNetwork(netid string) error {
 
 	var network models.Network
-	network, err := functions.GetParentNetwork(netid)
+	network, err := logic.GetParentNetwork(netid)
 	if err != nil {
 		return err
 	}
@@ -249,7 +249,7 @@ func updateNetwork(w http.ResponseWriter, r *http.Request) {
 	var params = mux.Vars(r)
 	var network models.Network
 	netname := params["networkname"]
-	network, err := functions.GetParentNetwork(netname)
+	network, err := logic.GetParentNetwork(netname)
 	if err != nil {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
@@ -260,21 +260,21 @@ func updateNetwork(w http.ResponseWriter, r *http.Request) {
 		returnErrorResponse(w, r, formatError(err, "badrequest"))
 		return
 	}
-	rangeupdate, localrangeupdate, err := network.Update(&newNetwork)
+	rangeupdate, localrangeupdate, err := logic.UpdateNetwork(&network, &newNetwork)
 	if err != nil {
 		returnErrorResponse(w, r, formatError(err, "badrequest"))
 		return
 	}
 
 	if rangeupdate {
-		err = functions.UpdateNetworkNodeAddresses(network.NetID)
+		err = logic.UpdateNetworkNodeAddresses(network.NetID)
 		if err != nil {
 			returnErrorResponse(w, r, formatError(err, "internal"))
 			return
 		}
 	}
 	if localrangeupdate {
-		err = functions.UpdateNetworkLocalAddresses(network.NetID)
+		err = logic.UpdateNetworkLocalAddresses(network.NetID)
 		if err != nil {
 			returnErrorResponse(w, r, formatError(err, "internal"))
 			return
@@ -290,7 +290,7 @@ func updateNetworkNodeLimit(w http.ResponseWriter, r *http.Request) {
 	var params = mux.Vars(r)
 	var network models.Network
 	netname := params["networkname"]
-	network, err := functions.GetParentNetwork(netname)
+	network, err := logic.GetParentNetwork(netname)
 	if err != nil {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
@@ -390,7 +390,7 @@ func CreateNetwork(network models.Network) error {
 	network.SetNetworkLastModified()
 	network.KeyUpdateTimeStamp = time.Now().Unix()
 
-	err := network.Validate(false)
+	err := logic.ValidateNetwork(&network, false)
 	if err != nil {
 		//returnErrorResponse(w, r, formatError(err, "badrequest"))
 		return err
@@ -425,7 +425,7 @@ func createAccessKey(w http.ResponseWriter, r *http.Request) {
 	var accesskey models.AccessKey
 	//start here
 	netname := params["networkname"]
-	network, err := functions.GetParentNetwork(netname)
+	network, err := logic.GetParentNetwork(netname)
 	if err != nil {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
@@ -604,7 +604,7 @@ func deleteAccessKey(w http.ResponseWriter, r *http.Request) {
 	w.WriteHeader(http.StatusOK)
 }
 func DeleteKey(keyname, netname string) error {
-	network, err := functions.GetParentNetwork(netname)
+	network, err := logic.GetParentNetwork(netname)
 	if err != nil {
 		return err
 	}

+ 2 - 1
controllers/networkHttpController_test.go

@@ -5,6 +5,7 @@ import (
 	"time"
 
 	"github.com/gravitl/netmaker/database"
+	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/models"
 	"github.com/stretchr/testify/assert"
 )
@@ -333,7 +334,7 @@ func TestValidateNetworkUpdate(t *testing.T) {
 
 func deleteAllNetworks() {
 	deleteAllNodes()
-	nets, _ := models.GetNetworks()
+	nets, _ := logic.GetNetworks()
 	for _, net := range nets {
 		DeleteNetwork(net.NetID)
 	}

+ 5 - 5
controllers/nodeGrpcController.go

@@ -35,7 +35,7 @@ func (s *NodeServiceServer) ReadNode(ctx context.Context, req *nodepb.Object) (*
 	if err != nil {
 		return nil, err
 	}
-	node.Update(&node)
+	logic.UpdateNode(&node, &node)
 	response := &nodepb.Object{
 		Data: string(nodeData),
 		Type: nodepb.NODE_TYPE,
@@ -55,8 +55,8 @@ func (s *NodeServiceServer) CreateNode(ctx context.Context, req *nodepb.Object)
 
 	//Check to see if key is valid
 	//TODO: Triple inefficient!!! This is the third call to the DB we make for networks
-	validKey := functions.IsKeyValid(node.Network, node.AccessKey)
-	network, err := functions.GetParentNetwork(node.Network)
+	validKey := logic.IsKeyValid(node.Network, node.AccessKey)
+	network, err := logic.GetParentNetwork(node.Network)
 	if err != nil {
 		return nil, err
 	}
@@ -99,11 +99,11 @@ func (s *NodeServiceServer) UpdateNode(ctx context.Context, req *nodepb.Object)
 	macaddress := newnode.MacAddress
 	networkName := newnode.Network
 
-	node, err := functions.GetNodeByMacAddress(networkName, macaddress)
+	node, err := logic.GetNodeByMacAddress(networkName, macaddress)
 	if err != nil {
 		return nil, err
 	}
-	err = node.Update(&newnode)
+	err = logic.UpdateNode(&node, &newnode)
 	if err != nil {
 		return nil, err
 	}

+ 27 - 23
controllers/nodeHttpController.go

@@ -9,7 +9,6 @@ import (
 
 	"github.com/gorilla/mux"
 	"github.com/gravitl/netmaker/database"
-	"github.com/gravitl/netmaker/dnslogic"
 	"github.com/gravitl/netmaker/functions"
 	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/models"
@@ -107,7 +106,7 @@ func authenticate(response http.ResponseWriter, request *http.Request) {
 				return
 			} else {
 				//Create a new JWT for the node
-				tokenString, _ := functions.CreateJWT(authRequest.MacAddress, result.Network)
+				tokenString, _ := logic.CreateJWT(authRequest.MacAddress, result.Network)
 
 				if tokenString == "" {
 					errorResponse.Code = http.StatusBadRequest
@@ -193,7 +192,7 @@ func authorize(networkCheck bool, authNetwork string, next http.Handler) http.Ha
 			//TODO: There's probably a better way of dealing with the "master token"/master password. Plz Help.
 			var isAuthorized = false
 			var macaddress = ""
-			username, networks, isadmin, errN := functions.VerifyUserToken(authToken)
+			username, networks, isadmin, errN := logic.VerifyUserToken(authToken)
 			isnetadmin := isadmin
 			if errN == nil && isadmin {
 				macaddress = "mastermac"
@@ -221,7 +220,7 @@ func authorize(networkCheck bool, authNetwork string, next http.Handler) http.Ha
 					if isnetadmin {
 						isAuthorized = true
 					} else {
-						node, err := functions.GetNodeByMacAddress(params["network"], macaddress)
+						node, err := logic.GetNodeByMacAddress(params["network"], macaddress)
 						if err != nil {
 							errorResponse = models.ErrorResponse{
 								Code: http.StatusUnauthorized, Message: "W1R3: Missing Auth Token.",
@@ -285,14 +284,14 @@ func getNetworkNodes(w http.ResponseWriter, r *http.Request) {
 //Not quite sure if this is necessary. Probably necessary based on front end but may want to review after iteration 1 if it's being used or not
 func getAllNodes(w http.ResponseWriter, r *http.Request) {
 	w.Header().Set("Content-Type", "application/json")
-	user, err := functions.GetUser(r.Header.Get("user"))
+	user, err := logic.GetUser(r.Header.Get("user"))
 	if err != nil && r.Header.Get("ismasterkey") != "yes" {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
 	}
 	var nodes []models.Node
 	if user.IsAdmin || r.Header.Get("ismasterkey") == "yes" {
-		nodes, err = models.GetAllNodes()
+		nodes, err = logic.GetAllNodes()
 		if err != nil {
 			returnErrorResponse(w, r, formatError(err, "internal"))
 			return
@@ -392,7 +391,7 @@ func createNode(w http.ResponseWriter, r *http.Request) {
 
 	node.Network = networkName
 
-	network, err := node.GetNetwork()
+	network, err := logic.GetNetworkByNode(&node)
 	if err != nil {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
@@ -400,7 +399,7 @@ func createNode(w http.ResponseWriter, r *http.Request) {
 
 	//Check to see if key is valid
 	//TODO: Triple inefficient!!! This is the third call to the DB we make for networks
-	validKey := functions.IsKeyValid(networkName, node.AccessKey)
+	validKey := logic.IsKeyValid(networkName, node.AccessKey)
 
 	if !validKey {
 		//Check to see if network will allow manual sign up
@@ -441,8 +440,9 @@ func uncordonNode(w http.ResponseWriter, r *http.Request) {
 	json.NewEncoder(w).Encode("SUCCESS")
 }
 
+// UncordonNode - approves a node to join a network
 func UncordonNode(network, macaddress string) (models.Node, error) {
-	node, err := functions.GetNodeByMacAddress(network, macaddress)
+	node, err := logic.GetNodeByMacAddress(network, macaddress)
 	if err != nil {
 		return models.Node{}, err
 	}
@@ -453,7 +453,7 @@ func UncordonNode(network, macaddress string) (models.Node, error) {
 	if err != nil {
 		return node, err
 	}
-	key, err := functions.GetRecordKey(node.MacAddress, node.Network)
+	key, err := logic.GetRecordKey(node.MacAddress, node.Network)
 	if err != nil {
 		return node, err
 	}
@@ -483,8 +483,9 @@ func createEgressGateway(w http.ResponseWriter, r *http.Request) {
 	json.NewEncoder(w).Encode(node)
 }
 
+// CreateEgressGateway - creates an egress gateway
 func CreateEgressGateway(gateway models.EgressGatewayRequest) (models.Node, error) {
-	node, err := functions.GetNodeByMacAddress(gateway.NetID, gateway.NodeID)
+	node, err := logic.GetNodeByMacAddress(gateway.NetID, gateway.NodeID)
 	if node.OS == "windows" || node.OS == "macos" { // add in darwin later
 		return models.Node{}, errors.New(node.OS + " is unsupported for egress gateways")
 	}
@@ -515,7 +516,7 @@ func CreateEgressGateway(gateway models.EgressGatewayRequest) (models.Node, erro
 			postDownCmd = node.PostDown + "; " + postDownCmd
 		}
 	}
-	key, err := functions.GetRecordKey(gateway.NodeID, gateway.NetID)
+	key, err := logic.GetRecordKey(gateway.NodeID, gateway.NetID)
 	if err != nil {
 		return node, err
 	}
@@ -565,9 +566,10 @@ func deleteEgressGateway(w http.ResponseWriter, r *http.Request) {
 	json.NewEncoder(w).Encode(node)
 }
 
+// DeleteEgressGateway - deletes egress from node
 func DeleteEgressGateway(network, macaddress string) (models.Node, error) {
 
-	node, err := functions.GetNodeByMacAddress(network, macaddress)
+	node, err := logic.GetNodeByMacAddress(network, macaddress)
 	if err != nil {
 		return models.Node{}, err
 	}
@@ -582,7 +584,7 @@ func DeleteEgressGateway(network, macaddress string) (models.Node, error) {
 	}
 	node.SetLastModified()
 	node.PullChanges = "yes"
-	key, err := functions.GetRecordKey(node.MacAddress, node.Network)
+	key, err := logic.GetRecordKey(node.MacAddress, node.Network)
 	if err != nil {
 		return models.Node{}, err
 	}
@@ -615,9 +617,10 @@ func createIngressGateway(w http.ResponseWriter, r *http.Request) {
 	json.NewEncoder(w).Encode(node)
 }
 
+// CreateIngressGateway - creates an ingress gateway
 func CreateIngressGateway(netid string, macaddress string) (models.Node, error) {
 
-	node, err := functions.GetNodeByMacAddress(netid, macaddress)
+	node, err := logic.GetNodeByMacAddress(netid, macaddress)
 	if node.OS == "windows" || node.OS == "macos" { // add in darwin later
 		return models.Node{}, errors.New(node.OS + " is unsupported for ingress gateways")
 	}
@@ -626,7 +629,7 @@ func CreateIngressGateway(netid string, macaddress string) (models.Node, error)
 		return models.Node{}, err
 	}
 
-	network, err := functions.GetParentNetwork(netid)
+	network, err := logic.GetParentNetwork(netid)
 	if err != nil {
 		return models.Node{}, err
 	}
@@ -649,7 +652,7 @@ func CreateIngressGateway(netid string, macaddress string) (models.Node, error)
 	node.PostDown = postDownCmd
 	node.PullChanges = "yes"
 	node.UDPHolePunch = "no"
-	key, err := functions.GetRecordKey(node.MacAddress, node.Network)
+	key, err := logic.GetRecordKey(node.MacAddress, node.Network)
 	if err != nil {
 		return models.Node{}, err
 	}
@@ -679,13 +682,14 @@ func deleteIngressGateway(w http.ResponseWriter, r *http.Request) {
 	json.NewEncoder(w).Encode(node)
 }
 
+// DeleteIngressGateway - deletes an ingress gateway
 func DeleteIngressGateway(networkName string, macaddress string) (models.Node, error) {
 
-	node, err := functions.GetNodeByMacAddress(networkName, macaddress)
+	node, err := logic.GetNodeByMacAddress(networkName, macaddress)
 	if err != nil {
 		return models.Node{}, err
 	}
-	network, err := functions.GetParentNetwork(networkName)
+	network, err := logic.GetParentNetwork(networkName)
 	if err != nil {
 		return models.Node{}, err
 	}
@@ -700,7 +704,7 @@ func DeleteIngressGateway(networkName string, macaddress string) (models.Node, e
 	node.IngressGatewayRange = ""
 	node.PullChanges = "yes"
 
-	key, err := functions.GetRecordKey(node.MacAddress, node.Network)
+	key, err := logic.GetRecordKey(node.MacAddress, node.Network)
 	if err != nil {
 		return models.Node{}, err
 	}
@@ -723,7 +727,7 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
 
 	var node models.Node
 	//start here
-	node, err := functions.GetNodeByMacAddress(params["network"], params["macaddress"])
+	node, err := logic.GetNodeByMacAddress(params["network"], params["macaddress"])
 	if err != nil {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
@@ -749,7 +753,7 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
 			}
 		}
 	}
-	err = node.Update(&newNode)
+	err = logic.UpdateNode(&node, &newNode)
 	if err != nil {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
@@ -762,7 +766,7 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
 	}
 
 	if servercfg.IsDNSMode() {
-		err = dnslogic.SetDNS()
+		err = logic.SetDNS()
 	}
 	if err != nil {
 		returnErrorResponse(w, r, formatError(err, "internal"))

+ 1 - 1
controllers/nodeHttpController_test.go

@@ -148,7 +148,7 @@ func TestValidateEgressGateway(t *testing.T) {
 ////func TestUpdateNode(t *testing.T) {
 ////}
 func deleteAllNodes() {
-	nodes, _ := models.GetAllNodes()
+	nodes, _ := logic.GetAllNodes()
 	for _, node := range nodes {
 		key := node.MacAddress + "###" + node.Network
 		DeleteNode(key, true)

+ 5 - 4
controllers/relay.go

@@ -9,6 +9,7 @@ import (
 	"github.com/gorilla/mux"
 	"github.com/gravitl/netmaker/database"
 	"github.com/gravitl/netmaker/functions"
+	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/models"
 )
 
@@ -35,7 +36,7 @@ func createRelay(w http.ResponseWriter, r *http.Request) {
 
 // CreateRelay - creates a relay
 func CreateRelay(relay models.RelayRequest) (models.Node, error) {
-	node, err := functions.GetNodeByMacAddress(relay.NetID, relay.NodeID)
+	node, err := logic.GetNodeByMacAddress(relay.NetID, relay.NodeID)
 	if node.OS == "windows" || node.OS == "macos" { // add in darwin later
 		return models.Node{}, errors.New(node.OS + " is unsupported for relay")
 	}
@@ -49,7 +50,7 @@ func CreateRelay(relay models.RelayRequest) (models.Node, error) {
 	node.IsRelay = "yes"
 	node.RelayAddrs = relay.RelayAddrs
 
-	key, err := functions.GetRecordKey(relay.NodeID, relay.NetID)
+	key, err := logic.GetRecordKey(relay.NodeID, relay.NetID)
 	if err != nil {
 		return node, err
 	}
@@ -147,7 +148,7 @@ func UpdateRelay(network string, oldAddrs []string, newAddrs []string) {
 // DeleteRelay - deletes a relay
 func DeleteRelay(network, macaddress string) (models.Node, error) {
 
-	node, err := functions.GetNodeByMacAddress(network, macaddress)
+	node, err := logic.GetNodeByMacAddress(network, macaddress)
 	if err != nil {
 		return models.Node{}, err
 	}
@@ -160,7 +161,7 @@ func DeleteRelay(network, macaddress string) (models.Node, error) {
 	node.RelayAddrs = []string{}
 	node.SetLastModified()
 	node.PullChanges = "yes"
-	key, err := functions.GetRecordKey(node.MacAddress, node.Network)
+	key, err := logic.GetRecordKey(node.MacAddress, node.Network)
 	if err != nil {
 		return models.Node{}, err
 	}

+ 2 - 2
controllers/responseHttp.go

@@ -2,9 +2,9 @@ package controller
 
 import (
 	"encoding/json"
-	"fmt"
 	"net/http"
 
+	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/models"
 )
 
@@ -48,7 +48,7 @@ func returnErrorResponse(response http.ResponseWriter, request *http.Request, er
 	if err != nil {
 		panic(err)
 	}
-	fmt.Println(errorMessage)
+	logic.Log("processed request error: "+errorMessage.Message, 1)
 	response.Header().Set("Content-Type", "application/json")
 	response.WriteHeader(errorMessage.Code)
 	response.Write(jsonResponse)

+ 2 - 2
controllers/serverHttpController.go

@@ -6,7 +6,7 @@ import (
 	"strings"
 
 	"github.com/gorilla/mux"
-	"github.com/gravitl/netmaker/functions"
+	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/servercfg"
 	"github.com/gravitl/netmaker/serverctl"
@@ -42,7 +42,7 @@ func securityCheckServer(adminonly bool, next http.Handler) http.HandlerFunc {
 		}
 		//all endpoints here require master so not as complicated
 		//still might not be a good  way of doing this
-		user, _, isadmin, err := functions.VerifyUserToken(authToken)
+		user, _, isadmin, err := logic.VerifyUserToken(authToken)
 		errorResponse = models.ErrorResponse{
 			Code: http.StatusUnauthorized, Message: "W1R3: You are unauthorized to access this endpoint.",
 		}

+ 48 - 219
controllers/userHttpController.go

@@ -7,12 +7,12 @@ import (
 	"net/http"
 	"strings"
 
-	"github.com/go-playground/validator/v10"
 	"github.com/gorilla/mux"
+	"github.com/gravitl/netmaker/auth"
 	"github.com/gravitl/netmaker/database"
 	"github.com/gravitl/netmaker/functions"
+	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/models"
-	"golang.org/x/crypto/bcrypt"
 )
 
 func userHandlers(r *mux.Router) {
@@ -21,11 +21,19 @@ 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/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/oauth/login", auth.HandleAuthLogin).Methods("GET")
+	r.HandleFunc("/api/oauth/callback", auth.HandleAuthCallback).Methods("GET")
+	r.HandleFunc("/api/oauth/error", throwOauthError).Methods("GET")
+}
+
+func throwOauthError(response http.ResponseWriter, request *http.Request) {
+	returnErrorResponse(response, request, formatError(errors.New("No token returned"), "unauthorized"))
 }
 
 // Node authenticates using its password and retrieves a JWT for authorization.
@@ -46,7 +54,7 @@ func authenticateUser(response http.ResponseWriter, request *http.Request) {
 		return
 	}
 
-	jwt, err := VerifyAuthRequest(authRequest)
+	jwt, err := logic.VerifyAuthRequest(authRequest)
 	if err != nil {
 		returnErrorResponse(response, request, formatError(err, "badrequest"))
 		return
@@ -79,35 +87,6 @@ func authenticateUser(response http.ResponseWriter, request *http.Request) {
 	response.Write(successJSONResponse)
 }
 
-// VerifyAuthRequest - verifies an auth request
-func VerifyAuthRequest(authRequest models.UserAuthParams) (string, error) {
-	var result models.User
-	if authRequest.UserName == "" {
-		return "", errors.New("username can't be empty")
-	} else if authRequest.Password == "" {
-		return "", errors.New("password can't be empty")
-	}
-	//Search DB for node with Mac Address. Ignore pending nodes (they should not be able to authenticate with API until approved).
-	record, err := database.FetchRecord(database.USERS_TABLE_NAME, authRequest.UserName)
-	if err != nil {
-		return "", errors.New("incorrect credentials")
-	}
-	if err = json.Unmarshal([]byte(record), &result); err != nil {
-		return "", errors.New("incorrect credentials")
-	}
-
-	// 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...
-	// TODO: Consider a way of hashing the password client side before sending, or using certificates
-	if err = bcrypt.CompareHashAndPassword([]byte(result.Password), []byte(authRequest.Password)); err != nil {
-		return "", errors.New("incorrect credentials")
-	}
-
-	//Create a new JWT for the node
-	tokenString, _ := functions.CreateUserJWT(authRequest.UserName, result.Networks, result.IsAdmin)
-	return tokenString, nil
-}
-
 // The middleware for most requests to the API
 // They all pass  through here first
 // This will validate the JWT (or check for master token)
@@ -164,7 +143,7 @@ func ValidateUserToken(token string, user string, adminonly bool) error {
 		return errors.New("Missing Auth Token.")
 	}
 
-	username, _, isadmin, err := functions.VerifyUserToken(authToken)
+	username, _, isadmin, err := logic.VerifyUserToken(authToken)
 	if err != nil {
 		return errors.New("Error Verifying Auth Token")
 	}
@@ -181,37 +160,11 @@ func ValidateUserToken(token string, user string, adminonly bool) error {
 	return nil
 }
 
-// HasAdmin - checks if server has an admin
-func HasAdmin() (bool, error) {
-
-	collection, err := database.FetchRecords(database.USERS_TABLE_NAME)
-	if err != nil {
-		if database.IsEmptyRecord(err) {
-			return false, nil
-		} else {
-			return true, err
-
-		}
-	}
-	for _, value := range collection { // filter for isadmin true
-		var user models.User
-		err = json.Unmarshal([]byte(value), &user)
-		if err != nil {
-			continue
-		}
-		if user.IsAdmin {
-			return true, nil
-		}
-	}
-
-	return false, err
-}
-
 func hasAdmin(w http.ResponseWriter, r *http.Request) {
 
 	w.Header().Set("Content-Type", "application/json")
 
-	hasadmin, err := HasAdmin()
+	hasadmin, err := logic.HasAdmin()
 	if err != nil {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
@@ -221,20 +174,6 @@ func hasAdmin(w http.ResponseWriter, r *http.Request) {
 
 }
 
-// GetUser - gets a user
-func GetUser(username string) (models.ReturnUser, error) {
-
-	var user models.ReturnUser
-	record, err := database.FetchRecord(database.USERS_TABLE_NAME, username)
-	if err != nil {
-		return user, err
-	}
-	if err = json.Unmarshal([]byte(record), &user); err != nil {
-		return models.ReturnUser{}, err
-	}
-	return user, err
-}
-
 // GetUserInternal - gets an internal user
 func GetUserInternal(username string) (models.User, error) {
 
@@ -249,30 +188,6 @@ func GetUserInternal(username string) (models.User, error) {
 	return user, err
 }
 
-// GetUsers - gets users
-func GetUsers() ([]models.ReturnUser, error) {
-
-	var users []models.ReturnUser
-
-	collection, err := database.FetchRecords(database.USERS_TABLE_NAME)
-
-	if err != nil {
-		return users, err
-	}
-
-	for _, value := range collection {
-
-		var user models.ReturnUser
-		err = json.Unmarshal([]byte(value), &user)
-		if err != nil {
-			continue // get users
-		}
-		users = append(users, user)
-	}
-
-	return users, err
-}
-
 // Get an individual node. Nothin fancy here folks.
 func getUser(w http.ResponseWriter, r *http.Request) {
 	// set header.
@@ -280,7 +195,7 @@ func getUser(w http.ResponseWriter, r *http.Request) {
 
 	var params = mux.Vars(r)
 	usernameFetched := params["username"]
-	user, err := GetUser(usernameFetched)
+	user, err := logic.GetUser(usernameFetched)
 
 	if err != nil {
 		returnErrorResponse(w, r, formatError(err, "internal"))
@@ -295,7 +210,7 @@ func getUsers(w http.ResponseWriter, r *http.Request) {
 	// set header.
 	w.Header().Set("Content-Type", "application/json")
 
-	users, err := GetUsers()
+	users, err := logic.GetUsers()
 
 	if err != nil {
 		returnErrorResponse(w, r, formatError(err, "internal"))
@@ -306,42 +221,6 @@ func getUsers(w http.ResponseWriter, r *http.Request) {
 	json.NewEncoder(w).Encode(users)
 }
 
-// CreateUser - creates a user
-func CreateUser(user models.User) (models.User, error) {
-	// check if user exists
-	if _, err := GetUser(user.UserName); err == nil {
-		return models.User{}, errors.New("user exists")
-	}
-	err := ValidateUser("create", user)
-	if err != nil {
-		return models.User{}, err
-	}
-
-	// encrypt that password so we never see it again
-	hash, err := bcrypt.GenerateFromPassword([]byte(user.Password), 5)
-	if err != nil {
-		return user, err
-	}
-	// set password to encrypted password
-	user.Password = string(hash)
-
-	tokenString, _ := functions.CreateUserJWT(user.UserName, user.Networks, user.IsAdmin)
-
-	if tokenString == "" {
-		// returnErrorResponse(w, r, errorResponse)
-		return user, err
-	}
-
-	// connect db
-	data, err := json.Marshal(&user)
-	if err != nil {
-		return user, err
-	}
-	err = database.Insert(user.UserName, string(data), database.USERS_TABLE_NAME)
-
-	return user, err
-}
-
 func createAdmin(w http.ResponseWriter, r *http.Request) {
 	w.Header().Set("Content-Type", "application/json")
 
@@ -349,7 +228,7 @@ func createAdmin(w http.ResponseWriter, r *http.Request) {
 	// get node from body of request
 	_ = json.NewDecoder(r.Body).Decode(&admin)
 
-	admin, err := CreateAdmin(admin)
+	admin, err := logic.CreateAdmin(admin)
 
 	if err != nil {
 		returnErrorResponse(w, r, formatError(err, "badrequest"))
@@ -359,18 +238,6 @@ func createAdmin(w http.ResponseWriter, r *http.Request) {
 	json.NewEncoder(w).Encode(admin)
 }
 
-func CreateAdmin(admin models.User) (models.User, error) {
-	hasadmin, err := HasAdmin()
-	if err != nil {
-		return models.User{}, err
-	}
-	if hasadmin {
-		return models.User{}, errors.New("admin user already exists")
-	}
-	admin.IsAdmin = true
-	return CreateUser(admin)
-}
-
 func createUser(w http.ResponseWriter, r *http.Request) {
 	w.Header().Set("Content-Type", "application/json")
 
@@ -378,7 +245,7 @@ func createUser(w http.ResponseWriter, r *http.Request) {
 	// get node from body of request
 	_ = json.NewDecoder(r.Body).Decode(&user)
 
-	user, err := CreateUser(user)
+	user, err := logic.CreateUser(user)
 
 	if err != nil {
 		returnErrorResponse(w, r, formatError(err, "badrequest"))
@@ -388,50 +255,32 @@ func createUser(w http.ResponseWriter, r *http.Request) {
 	json.NewEncoder(w).Encode(user)
 }
 
-// UpdateUser - updates a given user
-func UpdateUser(userchange models.User, user models.User) (models.User, error) {
-	//check if user exists
-	if _, err := GetUser(user.UserName); err != nil {
-		return models.User{}, err
-	}
-
-	err := ValidateUser("update", userchange)
+func updateUserNetworks(w http.ResponseWriter, r *http.Request) {
+	w.Header().Set("Content-Type", "application/json")
+	var params = mux.Vars(r)
+	var user models.User
+	// start here
+	username := params["username"]
+	user, err := GetUserInternal(username)
 	if err != nil {
-		return models.User{}, err
-	}
-
-	queryUser := user.UserName
-
-	if userchange.UserName != "" {
-		user.UserName = userchange.UserName
+		returnErrorResponse(w, r, formatError(err, "internal"))
+		return
 	}
-	if len(userchange.Networks) > 0 {
-		user.Networks = userchange.Networks
+	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
 	}
-	if userchange.Password != "" {
-		// encrypt that password so we never see it again
-		hash, err := bcrypt.GenerateFromPassword([]byte(userchange.Password), 5)
 
-		if err != nil {
-			return userchange, err
-		}
-		// set password to encrypted password
-		userchange.Password = string(hash)
-
-		user.Password = userchange.Password
-	}
-	if err = database.DeleteRecord(database.USERS_TABLE_NAME, queryUser); err != nil {
-		return models.User{}, err
-	}
-	data, err := json.Marshal(&user)
+	err = logic.UpdateUserNetworks(userchange.Networks, userchange.IsAdmin, &user)
 	if err != nil {
-		return models.User{}, err
-	}
-	if err = database.Insert(user.UserName, string(data), database.USERS_TABLE_NAME); err != nil {
-		return models.User{}, err
+		returnErrorResponse(w, r, formatError(err, "badrequest"))
+		return
 	}
-	functions.PrintUserLog(models.NODE_SERVER_NAME, "updated user "+queryUser, 1)
-	return user, nil
+	functions.PrintUserLog(username, "status was updated", 1)
+	json.NewEncoder(w).Encode(user)
 }
 
 func updateUser(w http.ResponseWriter, r *http.Request) {
@@ -445,6 +294,10 @@ func updateUser(w http.ResponseWriter, r *http.Request) {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
 	}
+	if auth.IsOauthUser(&user) == nil {
+		returnErrorResponse(w, r, formatError(fmt.Errorf("can not update user info for oauth user %s", username), "forbidden"))
+		return
+	}
 	var userchange models.User
 	// we decode our body request params
 	err = json.NewDecoder(r.Body).Decode(&userchange)
@@ -453,7 +306,7 @@ func updateUser(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 	userchange.Networks = nil
-	user, err = UpdateUser(userchange, user)
+	user, err = logic.UpdateUser(userchange, user)
 	if err != nil {
 		returnErrorResponse(w, r, formatError(err, "badrequest"))
 		return
@@ -473,6 +326,10 @@ func updateUserAdm(w http.ResponseWriter, r *http.Request) {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
 	}
+	if auth.IsOauthUser(&user) != nil {
+		returnErrorResponse(w, r, formatError(fmt.Errorf("can not update user info for oauth user"), "forbidden"))
+		return
+	}
 	var userchange models.User
 	// we decode our body request params
 	err = json.NewDecoder(r.Body).Decode(&userchange)
@@ -480,7 +337,7 @@ func updateUserAdm(w http.ResponseWriter, r *http.Request) {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
 	}
-	user, err = UpdateUser(userchange, user)
+	user, err = logic.UpdateUser(userchange, user)
 	if err != nil {
 		returnErrorResponse(w, r, formatError(err, "badrequest"))
 		return
@@ -489,20 +346,6 @@ func updateUserAdm(w http.ResponseWriter, r *http.Request) {
 	json.NewEncoder(w).Encode(user)
 }
 
-// DeleteUser - deletes a given user
-func DeleteUser(user string) (bool, error) {
-
-	if userRecord, err := database.FetchRecord(database.USERS_TABLE_NAME, user); err != nil || len(userRecord) == 0 {
-		return false, errors.New("user does not exist")
-	}
-
-	err := database.DeleteRecord(database.USERS_TABLE_NAME, user)
-	if err != nil {
-		return false, err
-	}
-	return true, nil
-}
-
 func deleteUser(w http.ResponseWriter, r *http.Request) {
 	// Set header
 	w.Header().Set("Content-Type", "application/json")
@@ -511,7 +354,7 @@ func deleteUser(w http.ResponseWriter, r *http.Request) {
 	var params = mux.Vars(r)
 
 	username := params["username"]
-	success, err := DeleteUser(username)
+	success, err := logic.DeleteUser(username)
 
 	if err != nil {
 		returnErrorResponse(w, r, formatError(err, "internal"))
@@ -524,17 +367,3 @@ func deleteUser(w http.ResponseWriter, r *http.Request) {
 	functions.PrintUserLog(username, "was deleted", 1)
 	json.NewEncoder(w).Encode(params["username"] + " deleted.")
 }
-
-// ValidateUser - validates a user model
-func ValidateUser(operation string, user models.User) error {
-
-	v := validator.New()
-	err := v.Struct(user)
-
-	if err != nil {
-		for _, e := range err.(validator.ValidationErrors) {
-			fmt.Println(e)
-		}
-	}
-	return err
-}

+ 46 - 45
controllers/userHttpController_test.go

@@ -4,52 +4,53 @@ import (
 	"testing"
 
 	"github.com/gravitl/netmaker/database"
+	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/models"
 	"github.com/stretchr/testify/assert"
 )
 
 func deleteAllUsers() {
-	users, _ := GetUsers()
+	users, _ := logic.GetUsers()
 	for _, user := range users {
-		DeleteUser(user.UserName)
+		logic.DeleteUser(user.UserName)
 	}
 }
 
 func TestHasAdmin(t *testing.T) {
 	//delete all current users
 	database.InitializeDatabase()
-	users, _ := GetUsers()
+	users, _ := logic.GetUsers()
 	for _, user := range users {
-		success, err := DeleteUser(user.UserName)
+		success, err := logic.DeleteUser(user.UserName)
 		assert.Nil(t, err)
 		assert.True(t, success)
 	}
 	t.Run("NoUser", func(t *testing.T) {
-		found, err := HasAdmin()
+		found, err := logic.HasAdmin()
 		assert.Nil(t, err)
 		assert.False(t, found)
 	})
 	t.Run("No admin user", func(t *testing.T) {
 		var user = models.User{"noadmin", "password", nil, false}
-		_, err := CreateUser(user)
+		_, err := logic.CreateUser(user)
 		assert.Nil(t, err)
-		found, err := HasAdmin()
+		found, err := logic.HasAdmin()
 		assert.Nil(t, err)
 		assert.False(t, found)
 	})
 	t.Run("admin user", func(t *testing.T) {
 		var user = models.User{"admin", "password", nil, true}
-		_, err := CreateUser(user)
+		_, err := logic.CreateUser(user)
 		assert.Nil(t, err)
-		found, err := HasAdmin()
+		found, err := logic.HasAdmin()
 		assert.Nil(t, err)
 		assert.True(t, found)
 	})
 	t.Run("multiple admins", func(t *testing.T) {
 		var user = models.User{"admin1", "password", nil, true}
-		_, err := CreateUser(user)
+		_, err := logic.CreateUser(user)
 		assert.Nil(t, err)
-		found, err := HasAdmin()
+		found, err := logic.HasAdmin()
 		assert.Nil(t, err)
 		assert.True(t, found)
 	})
@@ -60,12 +61,12 @@ func TestCreateUser(t *testing.T) {
 	deleteAllUsers()
 	user := models.User{"admin", "password", nil, true}
 	t.Run("NoUser", func(t *testing.T) {
-		admin, err := CreateUser(user)
+		admin, err := logic.CreateUser(user)
 		assert.Nil(t, err)
 		assert.Equal(t, user.UserName, admin.UserName)
 	})
 	t.Run("UserExists", func(t *testing.T) {
-		_, err := CreateUser(user)
+		_, err := logic.CreateUser(user)
 		assert.NotNil(t, err)
 		assert.EqualError(t, err, "user exists")
 	})
@@ -78,14 +79,14 @@ func TestCreateAdmin(t *testing.T) {
 	t.Run("NoAdmin", func(t *testing.T) {
 		user.UserName = "admin"
 		user.Password = "password"
-		admin, err := CreateAdmin(user)
+		admin, err := logic.CreateAdmin(user)
 		assert.Nil(t, err)
 		assert.Equal(t, user.UserName, admin.UserName)
 	})
 	t.Run("AdminExists", func(t *testing.T) {
 		user.UserName = "admin2"
 		user.Password = "password1"
-		admin, err := CreateAdmin(user)
+		admin, err := logic.CreateAdmin(user)
 		assert.EqualError(t, err, "admin user already exists")
 		assert.Equal(t, admin, models.User{})
 	})
@@ -95,14 +96,14 @@ func TestDeleteUser(t *testing.T) {
 	database.InitializeDatabase()
 	deleteAllUsers()
 	t.Run("NonExistent User", func(t *testing.T) {
-		deleted, err := DeleteUser("admin")
+		deleted, err := logic.DeleteUser("admin")
 		assert.EqualError(t, err, "user does not exist")
 		assert.False(t, deleted)
 	})
 	t.Run("Existing User", func(t *testing.T) {
 		user := models.User{"admin", "password", nil, true}
-		CreateUser(user)
-		deleted, err := DeleteUser("admin")
+		logic.CreateUser(user)
+		deleted, err := logic.DeleteUser("admin")
 		assert.Nil(t, err)
 		assert.True(t, deleted)
 	})
@@ -114,44 +115,44 @@ func TestValidateUser(t *testing.T) {
 	t.Run("Valid Create", func(t *testing.T) {
 		user.UserName = "admin"
 		user.Password = "validpass"
-		err := ValidateUser("create", user)
+		err := logic.ValidateUser(user)
 		assert.Nil(t, err)
 	})
 	t.Run("Valid Update", func(t *testing.T) {
 		user.UserName = "admin"
 		user.Password = "password"
-		err := ValidateUser("update", user)
+		err := logic.ValidateUser(user)
 		assert.Nil(t, err)
 	})
 	t.Run("Invalid UserName", func(t *testing.T) {
 		t.Skip()
 		user.UserName = "*invalid"
-		err := ValidateUser("create", user)
+		err := logic.ValidateUser(user)
 		assert.Error(t, err)
 		//assert.Contains(t, err.Error(), "Field validation for 'UserName' failed")
 	})
 	t.Run("Short UserName", func(t *testing.T) {
 		t.Skip()
 		user.UserName = "1"
-		err := ValidateUser("create", user)
+		err := logic.ValidateUser(user)
 		assert.NotNil(t, err)
 		//assert.Contains(t, err.Error(), "Field validation for 'UserName' failed")
 	})
 	t.Run("Empty UserName", func(t *testing.T) {
 		t.Skip()
 		user.UserName = ""
-		err := ValidateUser("create", user)
+		err := logic.ValidateUser(user)
 		assert.EqualError(t, err, "some string")
 		//assert.Contains(t, err.Error(), "Field validation for 'UserName' failed")
 	})
 	t.Run("EmptyPassword", func(t *testing.T) {
 		user.Password = ""
-		err := ValidateUser("create", user)
+		err := logic.ValidateUser(user)
 		assert.EqualError(t, err, "Key: 'User.Password' Error:Field validation for 'Password' failed on the 'required' tag")
 	})
 	t.Run("ShortPassword", func(t *testing.T) {
 		user.Password = "123"
-		err := ValidateUser("create", user)
+		err := logic.ValidateUser(user)
 		assert.EqualError(t, err, "Key: 'User.Password' Error:Field validation for 'Password' failed on the 'min' tag")
 	})
 }
@@ -160,14 +161,14 @@ func TestGetUser(t *testing.T) {
 	database.InitializeDatabase()
 	deleteAllUsers()
 	t.Run("NonExistantUser", func(t *testing.T) {
-		admin, err := GetUser("admin")
+		admin, err := logic.GetUser("admin")
 		assert.EqualError(t, err, "could not find any records")
 		assert.Equal(t, "", admin.UserName)
 	})
 	t.Run("UserExisits", func(t *testing.T) {
 		user := models.User{"admin", "password", nil, true}
-		CreateUser(user)
-		admin, err := GetUser("admin")
+		logic.CreateUser(user)
+		admin, err := logic.GetUser("admin")
 		assert.Nil(t, err)
 		assert.Equal(t, user.UserName, admin.UserName)
 	})
@@ -183,7 +184,7 @@ func TestGetUserInternal(t *testing.T) {
 	})
 	t.Run("UserExisits", func(t *testing.T) {
 		user := models.User{"admin", "password", nil, true}
-		CreateUser(user)
+		logic.CreateUser(user)
 		admin, err := GetUserInternal("admin")
 		assert.Nil(t, err)
 		assert.Equal(t, user.UserName, admin.UserName)
@@ -194,21 +195,21 @@ func TestGetUsers(t *testing.T) {
 	database.InitializeDatabase()
 	deleteAllUsers()
 	t.Run("NonExistantUser", func(t *testing.T) {
-		admin, err := GetUsers()
+		admin, err := logic.GetUsers()
 		assert.EqualError(t, err, "could not find any records")
 		assert.Equal(t, []models.ReturnUser(nil), admin)
 	})
 	t.Run("UserExisits", func(t *testing.T) {
 		user := models.User{"admin", "password", nil, true}
-		CreateUser(user)
-		admins, err := GetUsers()
+		logic.CreateUser(user)
+		admins, err := logic.GetUsers()
 		assert.Nil(t, err)
 		assert.Equal(t, user.UserName, admins[0].UserName)
 	})
 	t.Run("MulipleUsers", func(t *testing.T) {
 		user := models.User{"user", "password", nil, true}
-		CreateUser(user)
-		admins, err := GetUsers()
+		logic.CreateUser(user)
+		admins, err := logic.GetUsers()
 		assert.Nil(t, err)
 		for _, u := range admins {
 			if u.UserName == "admin" {
@@ -227,14 +228,14 @@ func TestUpdateUser(t *testing.T) {
 	user := models.User{"admin", "password", nil, true}
 	newuser := models.User{"hello", "world", []string{"wirecat, netmaker"}, true}
 	t.Run("NonExistantUser", func(t *testing.T) {
-		admin, err := UpdateUser(newuser, user)
+		admin, err := logic.UpdateUser(newuser, user)
 		assert.EqualError(t, err, "could not find any records")
 		assert.Equal(t, "", admin.UserName)
 	})
 
 	t.Run("UserExists", func(t *testing.T) {
-		CreateUser(user)
-		admin, err := UpdateUser(newuser, user)
+		logic.CreateUser(user)
+		admin, err := logic.UpdateUser(newuser, user)
 		assert.Nil(t, err)
 		assert.Equal(t, newuser.UserName, admin.UserName)
 	})
@@ -271,43 +272,43 @@ func TestVerifyAuthRequest(t *testing.T) {
 	t.Run("EmptyUserName", func(t *testing.T) {
 		authRequest.UserName = ""
 		authRequest.Password = "Password"
-		jwt, err := VerifyAuthRequest(authRequest)
+		jwt, err := logic.VerifyAuthRequest(authRequest)
 		assert.Equal(t, "", jwt)
 		assert.EqualError(t, err, "username can't be empty")
 	})
 	t.Run("EmptyPassword", func(t *testing.T) {
 		authRequest.UserName = "admin"
 		authRequest.Password = ""
-		jwt, err := VerifyAuthRequest(authRequest)
+		jwt, err := logic.VerifyAuthRequest(authRequest)
 		assert.Equal(t, "", jwt)
 		assert.EqualError(t, err, "password can't be empty")
 	})
 	t.Run("NonExistantUser", func(t *testing.T) {
 		authRequest.UserName = "admin"
 		authRequest.Password = "password"
-		jwt, err := VerifyAuthRequest(authRequest)
+		jwt, err := logic.VerifyAuthRequest(authRequest)
 		assert.Equal(t, "", jwt)
 		assert.EqualError(t, err, "incorrect credentials")
 	})
 	t.Run("Non-Admin", func(t *testing.T) {
 		user := models.User{"nonadmin", "somepass", nil, false}
-		CreateUser(user)
+		logic.CreateUser(user)
 		authRequest := models.UserAuthParams{"nonadmin", "somepass"}
-		jwt, err := VerifyAuthRequest(authRequest)
+		jwt, err := logic.VerifyAuthRequest(authRequest)
 		assert.NotNil(t, jwt)
 		assert.Nil(t, err)
 	})
 	t.Run("WrongPassword", func(t *testing.T) {
 		user := models.User{"admin", "password", nil, false}
-		CreateUser(user)
+		logic.CreateUser(user)
 		authRequest := models.UserAuthParams{"admin", "badpass"}
-		jwt, err := VerifyAuthRequest(authRequest)
+		jwt, err := logic.VerifyAuthRequest(authRequest)
 		assert.Equal(t, "", jwt)
 		assert.EqualError(t, err, "incorrect credentials")
 	})
 	t.Run("Success", func(t *testing.T) {
 		authRequest := models.UserAuthParams{"admin", "password"}
-		jwt, err := VerifyAuthRequest(authRequest)
+		jwt, err := logic.VerifyAuthRequest(authRequest)
 		assert.Nil(t, err)
 		assert.NotNil(t, jwt)
 	})

+ 6 - 2
database/database.go

@@ -39,6 +39,9 @@ const SERVERCONF_TABLE_NAME = "serverconf"
 // DATABASE_FILENAME - database file name
 const DATABASE_FILENAME = "netmaker.db"
 
+// GENERATED_TABLE_NAME - stores server generated k/v
+const GENERATED_TABLE_NAME = "generated"
+
 // == ERROR CONSTS ==
 
 // NO_RECORD - no singular result found
@@ -87,11 +90,11 @@ func getCurrentDB() map[string]interface{} {
 }
 
 func InitializeDatabase() error {
-	log.Println("connecting to", servercfg.GetDB())
+	log.Println("[netmaker] connecting to", servercfg.GetDB())
 	tperiod := time.Now().Add(10 * time.Second)
 	for {
 		if err := getCurrentDB()[INIT_DB].(func() error)(); err != nil {
-			log.Println("unable to connect to db, retrying . . .")
+			log.Println("[netmaker] unable to connect to db, retrying . . .")
 			if time.Now().After(tperiod) {
 				return err
 			}
@@ -114,6 +117,7 @@ func createTables() {
 	createTable(INT_CLIENTS_TABLE_NAME)
 	createTable(PEERS_TABLE_NAME)
 	createTable(SERVERCONF_TABLE_NAME)
+	createTable(GENERATED_TABLE_NAME)
 }
 
 func createTable(tableName string) error {

+ 2 - 2
docs/architecture.rst

@@ -104,7 +104,7 @@ If running in daemon mode, on a periodic basis (systemd timer), the netclient pe
 The check in process is what allows Netmaker to create dynamic mesh networks. As nodes are added to, removed from, and modified on the network, other nodes are notified, and make appropriate changes.
 
 
-Datavase (sqlite, rqlite, postgres)
+Database (sqlite, rqlite, postgres)
 -------------------------------------
 
 As of v0.8, Netmaker uses sqlite by default as a database. It can also use PostgreSQL, or rqlite, a distributed (RAFT consensus) databaseand. Netmaker interacts with this database to store and retrieve information about nodes, networks, and users. 
@@ -191,4 +191,4 @@ To manage DNS (optional), the node must have systemd-resolved. Systems that have
 Limitations
 =============
 
-Install limitations mostly include platform-specific dependencies. A failed netclient install should display information about which command is failing, or which libraries are missing. This can often be solved via machine upgrade, installing missing dependencies, or setting kernel headers on the machine for WireGuard (e.x.: `Installing Kernel Headers on Debian <https://stackoverflow.com/questions/62356581/wireguard-vpn-how-to-fix-operation-not-supported-if-it-worked-before>`_) 
+Install limitations mostly include platform-specific dependencies. A failed netclient install should display information about which command is failing, or which libraries are missing. This can often be solved via machine upgrade, installing missing dependencies, or setting kernel headers on the machine for WireGuard (e.x.: `Installing Kernel Headers on Debian <https://stackoverflow.com/questions/62356581/wireguard-vpn-how-to-fix-operation-not-supported-if-it-worked-before>`_) 

+ 1 - 1
docs/conf.py

@@ -22,7 +22,7 @@ copyright = '2021, Alex Feiszli'
 author = 'Alex Feiszli'
 
 # The full version, including alpha/beta/rc tags
-release = '0.8.4'
+release = '0.8.5'
 
 
 # -- General configuration ---------------------------------------------------

+ 3 - 423
functions/helpers.go

@@ -1,21 +1,15 @@
-//TODO: Consider restructuring  this file/folder    "github.com/gorilla/handlers"
-
-//It may make more sense to split into different files and not call it "helpers"
-
 package functions
 
 import (
-	"encoding/base64"
 	"encoding/json"
-	"errors"
 	"fmt"
 	"log"
 	"math/rand"
-	"net"
 	"strings"
 	"time"
 
 	"github.com/gravitl/netmaker/database"
+	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/servercfg"
 )
@@ -59,20 +53,6 @@ func ParseIntClient(value string) (models.IntClient, error) {
 //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
 
-// GetUser - gets a user
-func GetUser(username string) (models.User, error) {
-
-	var user models.User
-	record, err := database.FetchRecord(database.USERS_TABLE_NAME, username)
-	if err != nil {
-		return user, err
-	}
-	if err = json.Unmarshal([]byte(record), &user); err != nil {
-		return models.User{}, err
-	}
-	return user, err
-}
-
 // SliceContains - sees if a slice contains something
 func SliceContains(slice []string, item string) bool {
 	set := make(map[string]struct{}, len(slice))
@@ -84,60 +64,6 @@ func SliceContains(slice []string, item string) bool {
 	return ok
 }
 
-// CreateServerToken - creates a server token
-func CreateServerToken(netID string) (string, error) {
-	var network models.Network
-	var accesskey models.AccessKey
-
-	network, err := GetParentNetwork(netID)
-	if err != nil {
-		return "", err
-	}
-
-	var accessToken models.AccessToken
-	servervals := models.ServerConfig{}
-	if servercfg.GetPlatform() == "Kubernetes" {
-		log.Println("server on kubernetes")
-		servervals = models.ServerConfig{
-			APIConnString:  servercfg.GetPodIP() + ":" + servercfg.GetAPIPort(),
-			GRPCConnString: servercfg.GetPodIP() + ":" + servercfg.GetGRPCPort(),
-			GRPCSSL:        "off",
-		}
-	} else {
-		log.Println("server on linux")
-		servervals = models.ServerConfig{
-			APIConnString:   "127.0.0.1:" + servercfg.GetAPIPort(),
-			GRPCConnString:  "127.0.0.1:" + servercfg.GetGRPCPort(),
-			GRPCSSL:         "off",
-			CheckinInterval: servercfg.GetCheckinInterval(),
-		}
-	}
-	log.Println("APIConnString:", servervals.APIConnString)
-	log.Println("GRPCConnString:", servervals.GRPCConnString)
-	log.Println("GRPCSSL:", servervals.GRPCSSL)
-	accessToken.ServerConfig = servervals
-	accessToken.ClientConfig.Network = netID
-	accessToken.ClientConfig.Key = GenKey()
-
-	accesskey.Name = GenKeyName()
-	accesskey.Value = accessToken.ClientConfig.Key
-	accesskey.Uses = 1
-	tokenjson, err := json.Marshal(accessToken)
-	if err != nil {
-		return accesskey.AccessString, err
-	}
-	accesskey.AccessString = base64.StdEncoding.EncodeToString([]byte(tokenjson))
-	log.Println("accessstring:", accesskey.AccessString)
-	network.AccessKeys = append(network.AccessKeys, accesskey)
-	if data, err := json.Marshal(network); err != nil {
-		return "", err
-	} else {
-		database.Insert(netID, string(data), database.NETWORKS_TABLE_NAME)
-	}
-
-	return accesskey.AccessString, nil
-}
-
 // GetPeersList - gets peers for given network
 func GetPeersList(networkName string) ([]models.PeersResponse, error) {
 
@@ -214,51 +140,6 @@ func NetworkExists(name string) (bool, error) {
 	return len(network) > 0, nil
 }
 
-// GetRecordKey - get record key
-func GetRecordKey(id string, network string) (string, error) {
-	if id == "" || network == "" {
-		return "", errors.New("unable to get record key")
-	}
-	return id + "###" + network, nil
-}
-
-// UpdateNetworkNodeAddresses - updates network node addresses
-func UpdateNetworkNodeAddresses(networkName string) error {
-
-	collections, err := database.FetchRecords(database.NODES_TABLE_NAME)
-	if err != nil {
-		return err
-	}
-
-	for _, value := range collections {
-
-		var node models.Node
-		err := json.Unmarshal([]byte(value), &node)
-		if err != nil {
-			fmt.Println("error in node address assignment!")
-			return err
-		}
-		if node.Network == networkName {
-			ipaddr, iperr := UniqueAddress(networkName)
-			if iperr != nil {
-				fmt.Println("error in node  address assignment!")
-				return iperr
-			}
-
-			node.Address = ipaddr
-			node.PullChanges = "yes"
-			data, err := json.Marshal(&node)
-			if err != nil {
-				return err
-			}
-			node.SetID()
-			database.Insert(node.ID, string(data), database.NODES_TABLE_NAME)
-		}
-	}
-
-	return nil
-}
-
 // NetworkNodesUpdateAction - updates action of network nodes
 func NetworkNodesUpdateAction(networkName string, action string) error {
 
@@ -325,51 +206,12 @@ func NetworkNodesUpdatePullChanges(networkName string) error {
 	return nil
 }
 
-// UpdateNetworkLocalAddresses - updates network localaddresses
-func UpdateNetworkLocalAddresses(networkName string) error {
-
-	collection, err := database.FetchRecords(database.NODES_TABLE_NAME)
-
-	if err != nil {
-		return err
-	}
-
-	for _, value := range collection {
-
-		var node models.Node
-
-		err := json.Unmarshal([]byte(value), &node)
-		if err != nil {
-			fmt.Println("error in node address assignment!")
-			return err
-		}
-		if node.Network == networkName {
-			ipaddr, iperr := UniqueAddress(networkName)
-			if iperr != nil {
-				fmt.Println("error in node  address assignment!")
-				return iperr
-			}
-
-			node.Address = ipaddr
-			newNodeData, err := json.Marshal(&node)
-			if err != nil {
-				fmt.Println("error in node  address assignment!")
-				return err
-			}
-			node.SetID()
-			database.Insert(node.ID, string(newNodeData), database.NODES_TABLE_NAME)
-		}
-	}
-
-	return nil
-}
-
 // IsNetworkDisplayNameUnique - checks if network display name unique
 func IsNetworkDisplayNameUnique(name string) (bool, error) {
 
 	isunique := true
 
-	dbs, err := models.GetNetworks()
+	dbs, err := logic.GetNetworks()
 	if err != nil {
 		return database.IsEmptyRecord(err), err
 	}
@@ -417,38 +259,10 @@ func GetNetworkNonServerNodeCount(networkName string) (int, error) {
 	return count, nil
 }
 
-//Checks to see if access key is valid
-//Does so by checking against all keys and seeing if any have the same value
-//may want to hash values before comparing...consider this
-//TODO: No error handling!!!!
-
-// IsKeyValid - check if key is valid
-func IsKeyValid(networkname string, keyvalue string) bool {
-
-	network, _ := GetParentNetwork(networkname)
-	var key models.AccessKey
-	foundkey := false
-	isvalid := false
-
-	for i := len(network.AccessKeys) - 1; i >= 0; i-- {
-		currentkey := network.AccessKeys[i]
-		if currentkey.Value == keyvalue {
-			key = currentkey
-			foundkey = true
-		}
-	}
-	if foundkey {
-		if key.Uses > 0 {
-			isvalid = true
-		}
-	}
-	return isvalid
-}
-
 // IsKeyValidGlobal - checks if a key is valid globally
 func IsKeyValidGlobal(keyvalue string) bool {
 
-	networks, _ := models.GetNetworks()
+	networks, _ := logic.GetNetworks()
 	var key models.AccessKey
 	foundkey := false
 	isvalid := false
@@ -478,43 +292,10 @@ func IsKeyValidGlobal(keyvalue string) bool {
 //Should probably just be GetNetwork. kind of a dumb name.
 //Used in contexts where it's not the Parent network.
 
-// GetParentNetwork - get parent network
-func GetParentNetwork(networkname string) (models.Network, error) {
-
-	var network models.Network
-	networkData, err := database.FetchRecord(database.NETWORKS_TABLE_NAME, networkname)
-	if err != nil {
-		return network, err
-	}
-	if err = json.Unmarshal([]byte(networkData), &network); err != nil {
-		return models.Network{}, err
-	}
-	return network, nil
-}
-
-// IsIpNet - checks if valid ip
-func IsIpNet(host string) bool {
-	return net.ParseIP(host) != nil
-}
-
 //Similar to above but checks if Cidr range is valid
 //At least this guy's got some print statements
 //still not good error handling
 
-// IsIpCIDR - IsIpCIDR
-func IsIpCIDR(host string) bool {
-
-	ip, ipnet, err := net.ParseCIDR(host)
-
-	if err != nil {
-		fmt.Println(err)
-		fmt.Println("Address Range is not valid!")
-		return false
-	}
-
-	return ip != nil && ipnet != nil
-}
-
 //This  checks to  make sure a network name is valid.
 //Switch to REGEX?
 
@@ -557,59 +338,6 @@ func NameInNodeCharSet(name string) bool {
 	return true
 }
 
-//This returns a node based on its mac address.
-//The mac address acts as the Unique ID for nodes.
-//Is this a dumb thing to do? I thought it was cool but maybe it's dumb.
-//It doesn't really provide a tangible benefit over a random ID
-
-// GetNodeByMacAddress - gets a node by mac address
-func GetNodeByMacAddress(network string, macaddress string) (models.Node, error) {
-
-	var node models.Node
-
-	key, err := GetRecordKey(macaddress, network)
-	if err != nil {
-		return node, err
-	}
-
-	record, err := database.FetchRecord(database.NODES_TABLE_NAME, key)
-	if err != nil {
-		return models.Node{}, err
-	}
-
-	if err = json.Unmarshal([]byte(record), &node); err != nil {
-		return models.Node{}, err
-	}
-
-	node.SetDefaults()
-
-	return node, nil
-}
-
-// GetDeletedNodeByMacAddress - get a deleted node
-func GetDeletedNodeByMacAddress(network string, macaddress string) (models.Node, error) {
-
-	var node models.Node
-
-	key, err := GetRecordKey(macaddress, network)
-	if err != nil {
-		return node, err
-	}
-
-	record, err := database.FetchRecord(database.DELETED_NODES_TABLE_NAME, key)
-	if err != nil {
-		return models.Node{}, err
-	}
-
-	if err = json.Unmarshal([]byte(record), &node); err != nil {
-		return models.Node{}, err
-	}
-
-	node.SetDefaults()
-
-	return node, nil
-}
-
 // RemoveDeletedNode - remove deleted node
 func RemoveDeletedNode(nodeid string) bool {
 	return database.DeleteRecord(database.DELETED_NODES_TABLE_NAME, nodeid) == nil
@@ -668,82 +396,6 @@ func GetAllExtClients() ([]models.ExtClient, error) {
 	return extclients, nil
 }
 
-//This returns a unique address for a node to use
-//it iterates through the list of IP's in the subnet
-//and checks against all nodes to see if it's taken, until it finds one.
-//TODO: We do not handle a case where we run out of addresses.
-//We will need to handle that eventually
-
-// UniqueAddress - see if address is unique
-func UniqueAddress(networkName string) (string, error) {
-
-	var network models.Network
-	network, err := GetParentNetwork(networkName)
-	if err != nil {
-		fmt.Println("UniqueAddress encountered  an error")
-		return "666", err
-	}
-
-	offset := true
-	ip, ipnet, err := net.ParseCIDR(network.AddressRange)
-	if err != nil {
-		fmt.Println("UniqueAddress encountered  an error")
-		return "666", err
-	}
-	for ip := ip.Mask(ipnet.Mask); ipnet.Contains(ip); Inc(ip) {
-		if offset {
-			offset = false
-			continue
-		}
-		if networkName == "comms" {
-			if IsIPUnique(networkName, ip.String(), database.INT_CLIENTS_TABLE_NAME, false) {
-				return ip.String(), err
-			}
-		} else {
-			if IsIPUnique(networkName, ip.String(), database.NODES_TABLE_NAME, false) && IsIPUnique(networkName, ip.String(), database.EXT_CLIENT_TABLE_NAME, false) {
-				return ip.String(), err
-			}
-		}
-	}
-
-	//TODO
-	err1 := errors.New("ERROR: No unique addresses available. Check network subnet.")
-	return "W1R3: NO UNIQUE ADDRESSES AVAILABLE", err1
-}
-
-// UniqueAddress6 - see if ipv6 address is unique
-func UniqueAddress6(networkName string) (string, error) {
-
-	var network models.Network
-	network, err := GetParentNetwork(networkName)
-	if err != nil {
-		fmt.Println("Network Not Found")
-		return "", err
-	}
-	if network.IsDualStack == "no" {
-		return "", nil
-	}
-
-	offset := true
-	ip, ipnet, err := net.ParseCIDR(network.AddressRange6)
-	if err != nil {
-		fmt.Println("UniqueAddress6 encountered  an error")
-		return "666", err
-	}
-	for ip := ip.Mask(ipnet.Mask); ipnet.Contains(ip); Inc(ip) {
-		if offset {
-			offset = false
-			continue
-		}
-		if IsIPUnique(networkName, ip.String(), database.NODES_TABLE_NAME, true) {
-			return ip.String(), err
-		}
-	}
-	//TODO
-	err1 := errors.New("ERROR: No unique addresses available. Check network subnet.")
-	return "W1R3: NO UNIQUE ADDRESSES AVAILABLE", err1
-}
-
 // GenKey - generates access key
 func GenKey() string {
 
@@ -781,68 +433,6 @@ func GenKeyName() string {
 	return "key" + string(b)
 }
 
-// IsIPUnique - checks if an IP is unique
-func IsIPUnique(network string, ip string, tableName string, isIpv6 bool) bool {
-
-	isunique := true
-	collection, err := database.FetchRecords(tableName)
-
-	if err != nil {
-		return isunique
-	}
-
-	for _, value := range collection { // filter
-		var node models.Node
-		if err = json.Unmarshal([]byte(value), &node); err != nil {
-			continue
-		}
-		if isIpv6 {
-			if node.Address6 == ip && node.Network == network {
-				return false
-			}
-		} else {
-			if node.Address == ip && node.Network == network {
-				return false
-			}
-		}
-	}
-
-	return isunique
-}
-
-//called once key has been used by createNode
-//reduces value by one and deletes if necessary
-// DecrimentKey - decriments key uses
-func DecrimentKey(networkName string, keyvalue string) {
-
-	var network models.Network
-
-	network, err := GetParentNetwork(networkName)
-	if err != nil {
-		return
-	}
-
-	for i := len(network.AccessKeys) - 1; i >= 0; i-- {
-
-		currentkey := network.AccessKeys[i]
-		if currentkey.Value == keyvalue {
-			network.AccessKeys[i].Uses--
-			if network.AccessKeys[i].Uses < 1 {
-				network.AccessKeys = append(network.AccessKeys[:i],
-					network.AccessKeys[i+1:]...)
-				break
-			}
-		}
-	}
-
-	if newNetworkData, err := json.Marshal(&network); err != nil {
-		PrintUserLog(models.NODE_SERVER_NAME, "failed to decrement key", 2)
-		return
-	} else {
-		database.Insert(network.NetID, string(newNetworkData), database.NETWORKS_TABLE_NAME)
-	}
-}
-
 // DeleteKey - deletes a key
 func DeleteKey(network models.Network, i int) {
 
@@ -855,13 +445,3 @@ func DeleteKey(network models.Network, i int) {
 		database.Insert(network.NetID, string(networkData), database.NETWORKS_TABLE_NAME)
 	}
 }
-
-// Inc - increments an IP
-func Inc(ip net.IP) {
-	for j := len(ip) - 1; j >= 0; j-- {
-		ip[j]++
-		if ip[j] > 0 {
-			break
-		}
-	}
-}

+ 5 - 33
functions/local.go

@@ -1,10 +1,12 @@
 package functions
 
 import (
-	"io/ioutil"
 	"os"
+
+	"github.com/gravitl/netmaker/logic"
 )
 
+// FileExists - checks if file exists
 func FileExists(f string) bool {
 	info, err := os.Stat(f)
 	if os.IsNotExist(err) {
@@ -13,6 +15,7 @@ func FileExists(f string) bool {
 	return !info.IsDir()
 }
 
+// SetDNSDir - sets the dns directory of the system
 func SetDNSDir() error {
 	dir, err := os.Getwd()
 	if err != nil {
@@ -27,7 +30,7 @@ func SetDNSDir() error {
 	}
 	_, err = os.Stat(dir + "/config/dnsconfig/Corefile")
 	if os.IsNotExist(err) {
-		err = SetCorefile(".")
+		err = logic.SetCorefile(".")
 		if err != nil {
 			PrintUserLog("", err.Error(), 0)
 		}
@@ -41,34 +44,3 @@ func SetDNSDir() error {
 	}
 	return nil
 }
-
-func SetCorefile(domains string) error {
-	dir, err := os.Getwd()
-	if err != nil {
-		return err
-	}
-	_, err = os.Stat(dir + "/config/dnsconfig")
-	if os.IsNotExist(err) {
-		os.Mkdir(dir+"/config/dnsconfig", 744)
-	} else if err != nil {
-		PrintUserLog("", "couldnt find or create /config/dnsconfig", 0)
-		return err
-	}
-
-	corefile := domains + ` {
-    reload 15s
-    hosts /root/dnsconfig/netmaker.hosts {
-	fallthrough	
-    }
-    forward . 8.8.8.8 8.8.4.4
-    log
-}
-`
-	corebytes := []byte(corefile)
-
-	err = ioutil.WriteFile(dir+"/config/dnsconfig/Corefile", corebytes, 0644)
-	if err != nil {
-		return err
-	}
-	return err
-}

+ 4 - 3
go.mod

@@ -9,7 +9,7 @@ require (
 	github.com/gorilla/handlers v1.5.1
 	github.com/gorilla/mux v1.8.0
 	github.com/lib/pq v1.10.3
-	github.com/mattn/go-sqlite3 v1.14.8
+	github.com/mattn/go-sqlite3 v1.14.9
 	github.com/rqlite/gorqlite v0.0.0-20210514125552-08ff1e76b22f
 	github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
 	github.com/stretchr/testify v1.7.0
@@ -17,12 +17,13 @@ require (
 	github.com/urfave/cli/v2 v2.3.0
 	golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97
 	golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985 // indirect
+	golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be // indirect
 	golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e // indirect
 	golang.org/x/text v0.3.7-0.20210524175448-3115f89c4b99 // indirect
 	golang.zx2c4.com/wireguard v0.0.0-20210805125648-3957e9b9dd19 // indirect
 	golang.zx2c4.com/wireguard/wgctrl v0.0.0-20210913210325-91d1988e44de
 	google.golang.org/genproto v0.0.0-20210201151548-94839c025ad4 // indirect
 	google.golang.org/grpc v1.35.0
-	google.golang.org/protobuf v1.26.0
-	gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
+	google.golang.org/protobuf v1.27.1
+	gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c
 )

+ 5 - 3
go.sum

@@ -85,8 +85,8 @@ github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ic
 github.com/lib/pq v1.10.3 h1:v9QZf2Sn6AmjXtQeFpdoq/eaNtYP6IN+7lcrygsIAtg=
 github.com/lib/pq v1.10.3/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
 github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
-github.com/mattn/go-sqlite3 v1.14.8 h1:gDp86IdQsN/xWjIEmr9MF6o9mpksUgh0fu+9ByFxzIU=
-github.com/mattn/go-sqlite3 v1.14.8/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
+github.com/mattn/go-sqlite3 v1.14.9 h1:10HX2Td0ocZpYEjhilsuo6WWtUqttj2Kb0KtD86/KYA=
+github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
 github.com/mdlayher/ethtool v0.0.0-20210210192532-2b88debcdd43 h1:WgyLFv10Ov49JAQI/ZLUkCZ7VJS3r74hwFIGXJsgZlY=
 github.com/mdlayher/ethtool v0.0.0-20210210192532-2b88debcdd43/go.mod h1:+t7E0lkKfbBsebllff1xdTmyJt8lH37niI6kwFk9OTo=
 github.com/mdlayher/genetlink v1.0.0 h1:OoHN1OdyEIkScEmRgxLEe2M9U8ClMytqA5niynLtfj0=
@@ -244,8 +244,10 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD
 google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
 google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
 google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
-google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
+google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=

+ 62 - 0
logic/accesskeys.go

@@ -0,0 +1,62 @@
+package logic
+
+import (
+	"encoding/json"
+
+	"github.com/gravitl/netmaker/database"
+	"github.com/gravitl/netmaker/models"
+)
+
+// DecrimentKey - decriments key uses
+func DecrimentKey(networkName string, keyvalue string) {
+
+	var network models.Network
+
+	network, err := GetParentNetwork(networkName)
+	if err != nil {
+		return
+	}
+
+	for i := len(network.AccessKeys) - 1; i >= 0; i-- {
+
+		currentkey := network.AccessKeys[i]
+		if currentkey.Value == keyvalue {
+			network.AccessKeys[i].Uses--
+			if network.AccessKeys[i].Uses < 1 {
+				network.AccessKeys = append(network.AccessKeys[:i],
+					network.AccessKeys[i+1:]...)
+				break
+			}
+		}
+	}
+
+	if newNetworkData, err := json.Marshal(&network); err != nil {
+		Log("failed to decrement key", 2)
+		return
+	} else {
+		database.Insert(network.NetID, string(newNetworkData), database.NETWORKS_TABLE_NAME)
+	}
+}
+
+// IsKeyValid - check if key is valid
+func IsKeyValid(networkname string, keyvalue string) bool {
+
+	network, _ := GetParentNetwork(networkname)
+	var key models.AccessKey
+	foundkey := false
+	isvalid := false
+
+	for i := len(network.AccessKeys) - 1; i >= 0; i-- {
+		currentkey := network.AccessKeys[i]
+		if currentkey.Value == keyvalue {
+			key = currentkey
+			foundkey = true
+		}
+	}
+	if foundkey {
+		if key.Uses > 0 {
+			isvalid = true
+		}
+	}
+	return isvalid
+}

+ 267 - 0
logic/auth.go

@@ -0,0 +1,267 @@
+package logic
+
+import (
+	"encoding/json"
+	"errors"
+	"fmt"
+
+	"github.com/go-playground/validator/v10"
+	"github.com/gravitl/netmaker/database"
+	"github.com/gravitl/netmaker/models"
+	"golang.org/x/crypto/bcrypt"
+)
+
+// HasAdmin - checks if server has an admin
+func HasAdmin() (bool, error) {
+
+	collection, err := database.FetchRecords(database.USERS_TABLE_NAME)
+	if err != nil {
+		if database.IsEmptyRecord(err) {
+			return false, nil
+		} else {
+			return true, err
+		}
+	}
+	for _, value := range collection { // filter for isadmin true
+		var user models.User
+		err = json.Unmarshal([]byte(value), &user)
+		if err != nil {
+			continue
+		}
+		if user.IsAdmin {
+			return true, nil
+		}
+	}
+
+	return false, err
+}
+
+// GetReturnUser - gets a user
+func GetReturnUser(username string) (models.ReturnUser, error) {
+
+	var user models.ReturnUser
+	record, err := database.FetchRecord(database.USERS_TABLE_NAME, username)
+	if err != nil {
+		return user, err
+	}
+	if err = json.Unmarshal([]byte(record), &user); err != nil {
+		return models.ReturnUser{}, err
+	}
+	return user, err
+}
+
+// GetUsers - gets users
+func GetUsers() ([]models.ReturnUser, error) {
+
+	var users []models.ReturnUser
+
+	collection, err := database.FetchRecords(database.USERS_TABLE_NAME)
+
+	if err != nil {
+		return users, err
+	}
+
+	for _, value := range collection {
+
+		var user models.ReturnUser
+		err = json.Unmarshal([]byte(value), &user)
+		if err != nil {
+			continue // get users
+		}
+		users = append(users, user)
+	}
+
+	return users, err
+}
+
+// CreateUser - creates a user
+func CreateUser(user models.User) (models.User, error) {
+	// check if user exists
+	if _, err := GetUser(user.UserName); err == nil {
+		return models.User{}, errors.New("user exists")
+	}
+	var err = ValidateUser(user)
+	if err != nil {
+		return models.User{}, err
+	}
+
+	// encrypt that password so we never see it again
+	hash, err := bcrypt.GenerateFromPassword([]byte(user.Password), 5)
+	if err != nil {
+		return user, err
+	}
+	// set password to encrypted password
+	user.Password = string(hash)
+
+	tokenString, _ := CreateUserJWT(user.UserName, user.Networks, user.IsAdmin)
+
+	if tokenString == "" {
+		// returnErrorResponse(w, r, errorResponse)
+		return user, err
+	}
+
+	// connect db
+	data, err := json.Marshal(&user)
+	if err != nil {
+		return user, err
+	}
+	err = database.Insert(user.UserName, string(data), database.USERS_TABLE_NAME)
+
+	return user, err
+}
+
+// CreateAdmin - creates an admin user
+func CreateAdmin(admin models.User) (models.User, error) {
+	hasadmin, err := HasAdmin()
+	if err != nil {
+		return models.User{}, err
+	}
+	if hasadmin {
+		return models.User{}, errors.New("admin user already exists")
+	}
+	admin.IsAdmin = true
+	return CreateUser(admin)
+}
+
+// VerifyAuthRequest - verifies an auth request
+func VerifyAuthRequest(authRequest models.UserAuthParams) (string, error) {
+	var result models.User
+	if authRequest.UserName == "" {
+		return "", errors.New("username can't be empty")
+	} else if authRequest.Password == "" {
+		return "", errors.New("password can't be empty")
+	}
+	//Search DB for node with Mac Address. Ignore pending nodes (they should not be able to authenticate with API until approved).
+	record, err := database.FetchRecord(database.USERS_TABLE_NAME, authRequest.UserName)
+	if err != nil {
+		return "", errors.New("incorrect credentials")
+	}
+	if err = json.Unmarshal([]byte(record), &result); err != nil {
+		return "", errors.New("incorrect credentials")
+	}
+
+	// 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...
+	// TODO: Consider a way of hashing the password client side before sending, or using certificates
+	if err = bcrypt.CompareHashAndPassword([]byte(result.Password), []byte(authRequest.Password)); err != nil {
+		return "", errors.New("incorrect credentials")
+	}
+
+	//Create a new JWT for the node
+	tokenString, _ := CreateUserJWT(authRequest.UserName, result.Networks, result.IsAdmin)
+	return tokenString, nil
+}
+
+// UpdateUserNetworks - updates the networks of a given user
+func UpdateUserNetworks(newNetworks []string, isadmin bool, currentUser *models.User) error {
+	// check if user exists
+	if returnedUser, err := GetUser(currentUser.UserName); err != nil {
+		return err
+	} else if returnedUser.IsAdmin {
+		return fmt.Errorf("can not make changes to an admin user, attempted to change %s", returnedUser.UserName)
+	}
+	if isadmin {
+		currentUser.IsAdmin = true
+		currentUser.Networks = nil
+	} else {
+		currentUser.Networks = newNetworks
+	}
+
+	data, err := json.Marshal(currentUser)
+	if err != nil {
+		return err
+	}
+	if err = database.Insert(currentUser.UserName, string(data), database.USERS_TABLE_NAME); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// UpdateUser - updates a given user
+func UpdateUser(userchange models.User, user models.User) (models.User, error) {
+	//check if user exists
+	if _, err := GetUser(user.UserName); err != nil {
+		return models.User{}, err
+	}
+
+	err := ValidateUser(userchange)
+	if err != nil {
+		return models.User{}, err
+	}
+
+	queryUser := user.UserName
+
+	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)
+
+		if err != nil {
+			return userchange, err
+		}
+		// set password to encrypted password
+		userchange.Password = string(hash)
+
+		user.Password = userchange.Password
+	}
+	if err = database.DeleteRecord(database.USERS_TABLE_NAME, queryUser); err != nil {
+		return models.User{}, err
+	}
+	data, err := json.Marshal(&user)
+	if err != nil {
+		return models.User{}, err
+	}
+	if err = database.Insert(user.UserName, string(data), database.USERS_TABLE_NAME); err != nil {
+		return models.User{}, err
+	}
+	Log("updated user "+queryUser, 1)
+	return user, nil
+}
+
+// ValidateUser - validates a user model
+func ValidateUser(user models.User) error {
+
+	v := validator.New()
+	err := v.Struct(user)
+
+	if err != nil {
+		for _, e := range err.(validator.ValidationErrors) {
+			Log(e.Error(), 2)
+		}
+	}
+
+	return err
+}
+
+// DeleteUser - deletes a given user
+func DeleteUser(user string) (bool, error) {
+
+	if userRecord, err := database.FetchRecord(database.USERS_TABLE_NAME, user); err != nil || len(userRecord) == 0 {
+		return false, errors.New("user does not exist")
+	}
+
+	err := database.DeleteRecord(database.USERS_TABLE_NAME, user)
+	if err != nil {
+		return false, err
+	}
+	return true, nil
+}
+
+// FetchAuthSecret - manages secrets for oauth
+func FetchAuthSecret(key string, secret string) (string, error) {
+	var record, err = database.FetchRecord(database.GENERATED_TABLE_NAME, key)
+	if err != nil {
+		if err = database.Insert(key, secret, database.GENERATED_TABLE_NAME); err != nil {
+			return "", err
+		} else {
+			return secret, nil
+		}
+	}
+	return record, nil
+}

+ 37 - 4
dnslogic/dns.go → logic/dns.go

@@ -1,10 +1,11 @@
-package dnslogic
+package logic
 
 import (
 	"encoding/json"
+	"io/ioutil"
+	"os"
 
 	"github.com/gravitl/netmaker/database"
-	"github.com/gravitl/netmaker/functions"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/servercfg"
 	"github.com/txn2/txeh"
@@ -14,7 +15,7 @@ import (
 func SetDNS() error {
 	hostfile := txeh.Hosts{}
 	var corefilestring string
-	networks, err := models.GetNetworks()
+	networks, err := GetNetworks()
 	if err != nil && !database.IsEmptyRecord(err) {
 		return err
 	}
@@ -38,7 +39,7 @@ func SetDNS() error {
 		return err
 	}
 	if servercfg.IsSplitDNS() {
-		err = functions.SetCorefile(corefilestring)
+		err = SetCorefile(corefilestring)
 	}
 	return err
 }
@@ -106,3 +107,35 @@ func GetCustomDNS(network string) ([]models.DNSEntry, error) {
 
 	return dns, err
 }
+
+// SetCorefile - sets the core file of the system
+func SetCorefile(domains string) error {
+	dir, err := os.Getwd()
+	if err != nil {
+		return err
+	}
+	_, err = os.Stat(dir + "/config/dnsconfig")
+	if os.IsNotExist(err) {
+		os.Mkdir(dir+"/config/dnsconfig", 744)
+	} else if err != nil {
+		Log("couldnt find or create /config/dnsconfig", 0)
+		return err
+	}
+
+	corefile := domains + ` {
+    reload 15s
+    hosts /root/dnsconfig/netmaker.hosts {
+	fallthrough	
+    }
+    forward . 8.8.8.8 8.8.4.4
+    log
+}
+`
+	corebytes := []byte(corefile)
+
+	err = ioutil.WriteFile(dir+"/config/dnsconfig/Corefile", corebytes, 0644)
+	if err != nil {
+		return err
+	}
+	return err
+}

+ 28 - 3
logic/extpeers.go

@@ -4,7 +4,6 @@ import (
 	"encoding/json"
 
 	"github.com/gravitl/netmaker/database"
-	"github.com/gravitl/netmaker/functions"
 	"github.com/gravitl/netmaker/models"
 )
 
@@ -23,12 +22,12 @@ func GetExtPeersList(macaddress string, networkName string) ([]models.ExtPeersRe
 		var extClient models.ExtClient
 		err = json.Unmarshal([]byte(value), &peer)
 		if err != nil {
-			functions.PrintUserLog(models.NODE_SERVER_NAME, "failed to unmarshal peer", 2)
+			Log("failed to unmarshal peer when getting ext peer list", 2)
 			continue
 		}
 		err = json.Unmarshal([]byte(value), &extClient)
 		if err != nil {
-			functions.PrintUserLog(models.NODE_SERVER_NAME, "failed to unmarshal ext client", 2)
+			Log("failed to unmarshal ext client", 2)
 			continue
 		}
 		if extClient.Network == networkName && extClient.IngressGatewayID == macaddress {
@@ -37,3 +36,29 @@ func GetExtPeersList(macaddress string, networkName string) ([]models.ExtPeersRe
 	}
 	return peers, err
 }
+
+// ExtClient.GetEgressRangesOnNetwork - returns the egress ranges on network of ext client
+func GetEgressRangesOnNetwork(client *models.ExtClient) ([]string, error) {
+
+	var result []string
+	nodesData, err := database.FetchRecords(database.NODES_TABLE_NAME)
+	if err != nil {
+		return []string{}, err
+	}
+	for _, nodeData := range nodesData {
+		var currentNode models.Node
+		if err = json.Unmarshal([]byte(nodeData), &currentNode); err != nil {
+			continue
+		}
+		if currentNode.Network != client.Network {
+			continue
+		}
+		if currentNode.IsEgressGateway == "yes" { // add the egress gateway range(s) to the result
+			if len(currentNode.EgressGatewayRanges) > 0 {
+				result = append(result, currentNode.EgressGatewayRanges...)
+			}
+		}
+	}
+
+	return result, nil
+}

+ 1 - 1
functions/jwt.go → logic/jwts.go

@@ -1,4 +1,4 @@
-package functions
+package logic
 
 import (
 	"errors"

+ 0 - 110
logic/network.go

@@ -1,110 +0,0 @@
-package logic
-
-import (
-	"net"
-	"os/exec"
-	"strings"
-
-	"github.com/gravitl/netmaker/models"
-	"github.com/gravitl/netmaker/netclient/ncutils"
-)
-
-// GetLocalIP - gets the local ip
-func GetLocalIP(node models.Node) string {
-
-	var local string
-
-	ifaces, err := net.Interfaces()
-	if err != nil {
-		return local
-	}
-	_, localrange, err := net.ParseCIDR(node.LocalRange)
-	if err != nil {
-		return local
-	}
-
-	found := false
-	for _, i := range ifaces {
-		if i.Flags&net.FlagUp == 0 {
-			continue // interface down
-		}
-		if i.Flags&net.FlagLoopback != 0 {
-			continue // loopback interface
-		}
-		addrs, err := i.Addrs()
-		if err != nil {
-			return local
-		}
-		for _, addr := range addrs {
-			var ip net.IP
-			switch v := addr.(type) {
-			case *net.IPNet:
-				if !found {
-					ip = v.IP
-					local = ip.String()
-					if node.IsLocal == "yes" {
-						found = localrange.Contains(ip)
-					} else {
-						found = true
-					}
-				}
-			case *net.IPAddr:
-				if !found {
-					ip = v.IP
-					local = ip.String()
-					if node.IsLocal == "yes" {
-						found = localrange.Contains(ip)
-
-					} else {
-						found = true
-					}
-				}
-			}
-		}
-	}
-	return local
-}
-
-// == Private ==
-
-func deleteInterface(ifacename string, postdown string) error {
-	var err error
-	if !ncutils.IsKernel() {
-		err = RemoveConf(ifacename, true)
-	} else {
-		ipExec, errN := exec.LookPath("ip")
-		err = errN
-		if err != nil {
-			ncutils.PrintLog(err.Error(), 1)
-		}
-		_, err = ncutils.RunCmd(ipExec+" link del "+ifacename, false)
-		if postdown != "" {
-			runcmds := strings.Split(postdown, "; ")
-			err = ncutils.RunCmds(runcmds, true)
-		}
-	}
-	return err
-}
-
-func isInterfacePresent(iface string, address string) (string, bool) {
-	var interfaces []net.Interface
-	var err error
-	interfaces, err = net.Interfaces()
-	if err != nil {
-		Log("ERROR: could not read interfaces", 0)
-		return "", true
-	}
-	for _, currIface := range interfaces {
-		var currAddrs []net.Addr
-		currAddrs, err = currIface.Addrs()
-		if err != nil || len(currAddrs) == 0 {
-			continue
-		}
-		for _, addr := range currAddrs {
-			if strings.Contains(addr.String(), address) && currIface.Name != iface {
-				return currIface.Name, false
-			}
-		}
-	}
-	return "", true
-}

+ 461 - 0
logic/networks.go

@@ -0,0 +1,461 @@
+package logic
+
+import (
+	"encoding/json"
+	"errors"
+	"fmt"
+	"net"
+	"os/exec"
+	"strings"
+
+	"github.com/go-playground/validator/v10"
+	"github.com/gravitl/netmaker/database"
+	"github.com/gravitl/netmaker/models"
+	"github.com/gravitl/netmaker/netclient/ncutils"
+	"github.com/gravitl/netmaker/validation"
+)
+
+// GetNetworks - returns all networks from database
+func GetNetworks() ([]models.Network, error) {
+	var networks []models.Network
+
+	collection, err := database.FetchRecords(database.NETWORKS_TABLE_NAME)
+
+	if err != nil {
+		return networks, err
+	}
+
+	for _, value := range collection {
+		var network models.Network
+		if err := json.Unmarshal([]byte(value), &network); err != nil {
+			return networks, err
+		}
+		// add network our array
+		networks = append(networks, network)
+	}
+
+	return networks, err
+}
+
+// GetParentNetwork - get parent network
+func GetParentNetwork(networkname string) (models.Network, error) {
+
+	var network models.Network
+	networkData, err := database.FetchRecord(database.NETWORKS_TABLE_NAME, networkname)
+	if err != nil {
+		return network, err
+	}
+	if err = json.Unmarshal([]byte(networkData), &network); err != nil {
+		return models.Network{}, err
+	}
+	return network, nil
+}
+
+// UniqueAddress - see if address is unique
+func UniqueAddress(networkName string) (string, error) {
+
+	var network models.Network
+	network, err := GetParentNetwork(networkName)
+	if err != nil {
+		fmt.Println("UniqueAddress encountered  an error")
+		return "666", err
+	}
+
+	offset := true
+	ip, ipnet, err := net.ParseCIDR(network.AddressRange)
+	if err != nil {
+		fmt.Println("UniqueAddress encountered  an error")
+		return "666", err
+	}
+	for ip := ip.Mask(ipnet.Mask); ipnet.Contains(ip); Inc(ip) {
+		if offset {
+			offset = false
+			continue
+		}
+		if networkName == "comms" {
+			if IsIPUnique(networkName, ip.String(), database.INT_CLIENTS_TABLE_NAME, false) {
+				return ip.String(), err
+			}
+		} else {
+			if IsIPUnique(networkName, ip.String(), database.NODES_TABLE_NAME, false) && IsIPUnique(networkName, ip.String(), database.EXT_CLIENT_TABLE_NAME, false) {
+				return ip.String(), err
+			}
+		}
+	}
+
+	//TODO
+	err1 := errors.New("ERROR: No unique addresses available. Check network subnet.")
+	return "W1R3: NO UNIQUE ADDRESSES AVAILABLE", err1
+}
+
+// IsIPUnique - checks if an IP is unique
+func IsIPUnique(network string, ip string, tableName string, isIpv6 bool) bool {
+
+	isunique := true
+	collection, err := database.FetchRecords(tableName)
+
+	if err != nil {
+		return isunique
+	}
+
+	for _, value := range collection { // filter
+		var node models.Node
+		if err = json.Unmarshal([]byte(value), &node); err != nil {
+			continue
+		}
+		if isIpv6 {
+			if node.Address6 == ip && node.Network == network {
+				return false
+			}
+		} else {
+			if node.Address == ip && node.Network == network {
+				return false
+			}
+		}
+	}
+
+	return isunique
+}
+
+// UniqueAddress6 - see if ipv6 address is unique
+func UniqueAddress6(networkName string) (string, error) {
+
+	var network models.Network
+	network, err := GetParentNetwork(networkName)
+	if err != nil {
+		fmt.Println("Network Not Found")
+		return "", err
+	}
+	if network.IsDualStack == "no" {
+		return "", nil
+	}
+
+	offset := true
+	ip, ipnet, err := net.ParseCIDR(network.AddressRange6)
+	if err != nil {
+		fmt.Println("UniqueAddress6 encountered  an error")
+		return "666", err
+	}
+	for ip := ip.Mask(ipnet.Mask); ipnet.Contains(ip); Inc(ip) {
+		if offset {
+			offset = false
+			continue
+		}
+		if IsIPUnique(networkName, ip.String(), database.NODES_TABLE_NAME, true) {
+			return ip.String(), err
+		}
+	}
+	//TODO
+	err1 := errors.New("ERROR: No unique addresses available. Check network subnet.")
+	return "W1R3: NO UNIQUE ADDRESSES AVAILABLE", err1
+}
+
+// GetLocalIP - gets the local ip
+func GetLocalIP(node models.Node) string {
+
+	var local string
+
+	ifaces, err := net.Interfaces()
+	if err != nil {
+		return local
+	}
+	_, localrange, err := net.ParseCIDR(node.LocalRange)
+	if err != nil {
+		return local
+	}
+
+	found := false
+	for _, i := range ifaces {
+		if i.Flags&net.FlagUp == 0 {
+			continue // interface down
+		}
+		if i.Flags&net.FlagLoopback != 0 {
+			continue // loopback interface
+		}
+		addrs, err := i.Addrs()
+		if err != nil {
+			return local
+		}
+		for _, addr := range addrs {
+			var ip net.IP
+			switch v := addr.(type) {
+			case *net.IPNet:
+				if !found {
+					ip = v.IP
+					local = ip.String()
+					if node.IsLocal == "yes" {
+						found = localrange.Contains(ip)
+					} else {
+						found = true
+					}
+				}
+			case *net.IPAddr:
+				if !found {
+					ip = v.IP
+					local = ip.String()
+					if node.IsLocal == "yes" {
+						found = localrange.Contains(ip)
+
+					} else {
+						found = true
+					}
+				}
+			}
+		}
+	}
+	return local
+}
+
+// UpdateNetworkLocalAddresses - updates network localaddresses
+func UpdateNetworkLocalAddresses(networkName string) error {
+
+	collection, err := database.FetchRecords(database.NODES_TABLE_NAME)
+
+	if err != nil {
+		return err
+	}
+
+	for _, value := range collection {
+
+		var node models.Node
+
+		err := json.Unmarshal([]byte(value), &node)
+		if err != nil {
+			fmt.Println("error in node address assignment!")
+			return err
+		}
+		if node.Network == networkName {
+			ipaddr, iperr := UniqueAddress(networkName)
+			if iperr != nil {
+				fmt.Println("error in node  address assignment!")
+				return iperr
+			}
+
+			node.Address = ipaddr
+			newNodeData, err := json.Marshal(&node)
+			if err != nil {
+				fmt.Println("error in node  address assignment!")
+				return err
+			}
+			node.SetID()
+			database.Insert(node.ID, string(newNodeData), database.NODES_TABLE_NAME)
+		}
+	}
+
+	return nil
+}
+
+// UpdateNetworkNodeAddresses - updates network node addresses
+func UpdateNetworkNodeAddresses(networkName string) error {
+
+	collections, err := database.FetchRecords(database.NODES_TABLE_NAME)
+	if err != nil {
+		return err
+	}
+
+	for _, value := range collections {
+
+		var node models.Node
+		err := json.Unmarshal([]byte(value), &node)
+		if err != nil {
+			fmt.Println("error in node address assignment!")
+			return err
+		}
+		if node.Network == networkName {
+			ipaddr, iperr := UniqueAddress(networkName)
+			if iperr != nil {
+				fmt.Println("error in node  address assignment!")
+				return iperr
+			}
+
+			node.Address = ipaddr
+			node.PullChanges = "yes"
+			data, err := json.Marshal(&node)
+			if err != nil {
+				return err
+			}
+			node.SetID()
+			database.Insert(node.ID, string(data), database.NODES_TABLE_NAME)
+		}
+	}
+
+	return nil
+}
+
+// IsNetworkDisplayNameUnique - checks if displayname is unique from other networks
+func IsNetworkDisplayNameUnique(network *models.Network) (bool, error) {
+
+	isunique := true
+
+	records, err := GetNetworks()
+
+	if err != nil && !database.IsEmptyRecord(err) {
+		return false, err
+	}
+
+	for i := 0; i < len(records); i++ {
+
+		if network.NetID == records[i].DisplayName {
+			isunique = false
+		}
+	}
+
+	return isunique, nil
+}
+
+// IsNetworkNameUnique - checks to see if any other networks have the same name (id)
+func IsNetworkNameUnique(network *models.Network) (bool, error) {
+
+	isunique := true
+
+	dbs, err := GetNetworks()
+
+	if err != nil && !database.IsEmptyRecord(err) {
+		return false, err
+	}
+
+	for i := 0; i < len(dbs); i++ {
+
+		if network.NetID == dbs[i].NetID {
+			isunique = false
+		}
+	}
+
+	return isunique, nil
+}
+
+// UpdateNetwork - updates a network with another network's fields
+func UpdateNetwork(currentNetwork *models.Network, newNetwork *models.Network) (bool, bool, error) {
+	if err := ValidateNetwork(newNetwork, true); err != nil {
+		return false, false, err
+	}
+	if newNetwork.NetID == currentNetwork.NetID {
+		hasrangeupdate := newNetwork.AddressRange != currentNetwork.AddressRange
+		localrangeupdate := newNetwork.LocalRange != currentNetwork.LocalRange
+		data, err := json.Marshal(newNetwork)
+		if err != nil {
+			return false, false, err
+		}
+		newNetwork.SetNetworkLastModified()
+		err = database.Insert(newNetwork.NetID, string(data), database.NETWORKS_TABLE_NAME)
+		return hasrangeupdate, localrangeupdate, err
+	}
+	// copy values
+	return false, false, errors.New("failed to update network " + newNetwork.NetID + ", cannot change netid.")
+}
+
+// Inc - increments an IP
+func Inc(ip net.IP) {
+	for j := len(ip) - 1; j >= 0; j-- {
+		ip[j]++
+		if ip[j] > 0 {
+			break
+		}
+	}
+}
+
+// GetNetwork - gets a network from database
+func GetNetwork(networkname string) (models.Network, error) {
+
+	var network models.Network
+	networkData, err := database.FetchRecord(database.NETWORKS_TABLE_NAME, networkname)
+	if err != nil {
+		return network, err
+	}
+	if err = json.Unmarshal([]byte(networkData), &network); err != nil {
+		return models.Network{}, err
+	}
+	return network, nil
+}
+
+// Network.NetIDInNetworkCharSet - checks if a netid of a network uses valid characters
+func NetIDInNetworkCharSet(network *models.Network) bool {
+
+	charset := "abcdefghijklmnopqrstuvwxyz1234567890-_."
+
+	for _, char := range network.NetID {
+		if !strings.Contains(charset, strings.ToLower(string(char))) {
+			return false
+		}
+	}
+	return true
+}
+
+// Network.Validate - validates fields of an network struct
+func ValidateNetwork(network *models.Network, isUpdate bool) error {
+	v := validator.New()
+	_ = v.RegisterValidation("netid_valid", func(fl validator.FieldLevel) bool {
+		inCharSet := NetIDInNetworkCharSet(network)
+		if isUpdate {
+			return inCharSet
+		}
+		isFieldUnique, _ := IsNetworkNameUnique(network)
+		return isFieldUnique && inCharSet
+	})
+	//
+	_ = v.RegisterValidation("displayname_valid", func(fl validator.FieldLevel) bool {
+		isFieldUnique, _ := IsNetworkDisplayNameUnique(network)
+		inCharSet := network.DisplayNameInNetworkCharSet()
+		if isUpdate {
+			return inCharSet
+		}
+		return isFieldUnique && inCharSet
+	})
+	_ = v.RegisterValidation("checkyesorno", func(fl validator.FieldLevel) bool {
+		return validation.CheckYesOrNo(fl)
+	})
+	err := v.Struct(network)
+	if err != nil {
+		for _, e := range err.(validator.ValidationErrors) {
+			fmt.Println(e)
+		}
+	}
+
+	return err
+}
+
+// == Private ==
+
+func deleteInterface(ifacename string, postdown string) error {
+	var err error
+	if !ncutils.IsKernel() {
+		err = RemoveConf(ifacename, true)
+	} else {
+		ipExec, errN := exec.LookPath("ip")
+		err = errN
+		if err != nil {
+			Log(err.Error(), 1)
+		}
+		_, err = ncutils.RunCmd(ipExec+" link del "+ifacename, false)
+		if postdown != "" {
+			runcmds := strings.Split(postdown, "; ")
+			err = ncutils.RunCmds(runcmds, false)
+		}
+	}
+	return err
+}
+
+func isInterfacePresent(iface string, address string) (string, bool) {
+	var interfaces []net.Interface
+	var err error
+	interfaces, err = net.Interfaces()
+	if err != nil {
+		Log("ERROR: could not read interfaces", 0)
+		return "", true
+	}
+	for _, currIface := range interfaces {
+		var currAddrs []net.Addr
+		currAddrs, err = currIface.Addrs()
+		if err != nil || len(currAddrs) == 0 {
+			continue
+		}
+		for _, addr := range currAddrs {
+			if strings.Contains(addr.String(), address) && currIface.Name != iface {
+				Log("found iface "+addr.String()+" "+currIface.Name, 2)
+				return currIface.Name, false
+			}
+		}
+	}
+	Log("failed to find iface "+iface, 2)
+	return "", true
+}

+ 263 - 2
logic/nodes.go

@@ -2,12 +2,15 @@ package logic
 
 import (
 	"encoding/json"
+	"errors"
+	"fmt"
 	"sort"
 	"time"
 
+	"github.com/go-playground/validator/v10"
 	"github.com/gravitl/netmaker/database"
-	"github.com/gravitl/netmaker/functions"
 	"github.com/gravitl/netmaker/models"
+	"github.com/gravitl/netmaker/validation"
 )
 
 // GetNetworkNodes - gets the nodes of a network
@@ -80,7 +83,7 @@ func GetPeers(node models.Node) ([]models.Node, error) {
 func IsLeader(node *models.Node) bool {
 	nodes, err := GetSortedNetworkServerNodes(node.Network)
 	if err != nil {
-		functions.PrintUserLog("", "ERROR: COULD NOT RETRIEVE SERVER NODES. THIS WILL BREAK HOLE PUNCHING.", 0)
+		Log("ERROR: COULD NOT RETRIEVE SERVER NODES. THIS WILL BREAK HOLE PUNCHING.", 0)
 		return false
 	}
 	for _, n := range nodes {
@@ -90,3 +93,261 @@ func IsLeader(node *models.Node) bool {
 	}
 	return len(nodes) <= 1 || nodes[1].Address == node.Address
 }
+
+// == DB related functions ==
+
+// UpdateNode - takes a node and updates another node with it's values
+func UpdateNode(currentNode *models.Node, newNode *models.Node) error {
+	newNode.Fill(currentNode)
+	if err := ValidateNode(newNode, true); err != nil {
+		return err
+	}
+	newNode.SetID()
+	if newNode.ID == currentNode.ID {
+		newNode.SetLastModified()
+		if data, err := json.Marshal(newNode); err != nil {
+			return err
+		} else {
+			return database.Insert(newNode.ID, string(data), database.NODES_TABLE_NAME)
+		}
+	}
+	return fmt.Errorf("failed to update node " + newNode.MacAddress + ", cannot change macaddress.")
+}
+
+func IsNodeIDUnique(node *models.Node) (bool, error) {
+	_, err := database.FetchRecord(database.NODES_TABLE_NAME, node.ID)
+	return database.IsEmptyRecord(err), err
+}
+
+func ValidateNode(node *models.Node, isUpdate bool) error {
+	v := validator.New()
+	_ = v.RegisterValidation("macaddress_unique", func(fl validator.FieldLevel) bool {
+		if isUpdate {
+			return true
+		}
+		isFieldUnique, _ := IsNodeIDUnique(node)
+		return isFieldUnique
+	})
+	_ = v.RegisterValidation("network_exists", func(fl validator.FieldLevel) bool {
+		_, err := GetNetworkByNode(node)
+		return err == nil
+	})
+	_ = v.RegisterValidation("in_charset", func(fl validator.FieldLevel) bool {
+		isgood := node.NameInNodeCharSet()
+		return isgood
+	})
+	_ = v.RegisterValidation("checkyesorno", func(fl validator.FieldLevel) bool {
+		return validation.CheckYesOrNo(fl)
+	})
+	err := v.Struct(node)
+
+	return err
+}
+
+// GetAllNodes - returns all nodes in the DB
+func GetAllNodes() ([]models.Node, error) {
+	var nodes []models.Node
+
+	collection, err := database.FetchRecords(database.NODES_TABLE_NAME)
+	if err != nil {
+		if database.IsEmptyRecord(err) {
+			return []models.Node{}, nil
+		}
+		return []models.Node{}, err
+	}
+
+	for _, value := range collection {
+		var node models.Node
+		if err := json.Unmarshal([]byte(value), &node); err != nil {
+			return []models.Node{}, err
+		}
+		// add node to our array
+		nodes = append(nodes, node)
+	}
+
+	return nodes, nil
+}
+
+// CheckIsServer - check if a node is the server node
+func CheckIsServer(node *models.Node) bool {
+	nodeData, err := database.FetchRecords(database.NODES_TABLE_NAME)
+	if err != nil && !database.IsEmptyRecord(err) {
+		return false
+	}
+	for _, value := range nodeData {
+		var tmpNode models.Node
+		if err := json.Unmarshal([]byte(value), &tmpNode); err != nil {
+			continue
+		}
+		if tmpNode.Network == node.Network && tmpNode.MacAddress != node.MacAddress {
+			return false
+		}
+	}
+	return true
+}
+
+// GetNetworkByNode - gets the network model from a node
+func GetNetworkByNode(node *models.Node) (models.Network, error) {
+
+	var network models.Network
+	networkData, err := database.FetchRecord(database.NETWORKS_TABLE_NAME, node.Network)
+	if err != nil {
+		return network, err
+	}
+	if err = json.Unmarshal([]byte(networkData), &network); err != nil {
+		return models.Network{}, err
+	}
+	return network, nil
+}
+
+// SetNodeDefaults - sets the defaults of a node to avoid empty fields
+func SetNodeDefaults(node *models.Node) {
+
+	//TODO: Maybe I should make Network a part of the node struct. Then we can just query the Network object for stuff.
+	parentNetwork, _ := GetNetworkByNode(node)
+
+	node.ExpirationDateTime = time.Now().Unix() + models.TEN_YEARS_IN_SECONDS
+
+	if node.ListenPort == 0 {
+		node.ListenPort = parentNetwork.DefaultListenPort
+	}
+	if node.SaveConfig == "" {
+		if parentNetwork.DefaultSaveConfig != "" {
+			node.SaveConfig = parentNetwork.DefaultSaveConfig
+		} else {
+			node.SaveConfig = "yes"
+		}
+	}
+	if node.Interface == "" {
+		node.Interface = parentNetwork.DefaultInterface
+	}
+	if node.PersistentKeepalive == 0 {
+		node.PersistentKeepalive = parentNetwork.DefaultKeepalive
+	}
+	if node.PostUp == "" {
+		postup := parentNetwork.DefaultPostUp
+		node.PostUp = postup
+	}
+	if node.IsStatic == "" {
+		node.IsStatic = "no"
+	}
+	if node.UDPHolePunch == "" {
+		node.UDPHolePunch = parentNetwork.DefaultUDPHolePunch
+		if node.UDPHolePunch == "" {
+			node.UDPHolePunch = "yes"
+		}
+	}
+	// == Parent Network settings ==
+	if node.IsDualStack == "" {
+		node.IsDualStack = parentNetwork.IsDualStack
+	}
+	if node.MTU == 0 {
+		node.MTU = parentNetwork.DefaultMTU
+	}
+	// == node defaults if not set by parent ==
+	node.SetIPForwardingDefault()
+	node.SetDNSOnDefault()
+	node.SetIsLocalDefault()
+	node.SetIsDualStackDefault()
+	node.SetLastModified()
+	node.SetDefaultName()
+	node.SetLastCheckIn()
+	node.SetLastPeerUpdate()
+	node.SetRoamingDefault()
+	node.SetPullChangesDefault()
+	node.SetDefaultAction()
+	node.SetID()
+	node.SetIsServerDefault()
+	node.SetIsStaticDefault()
+	node.SetDefaultEgressGateway()
+	node.SetDefaultIngressGateway()
+	node.SetDefaulIsPending()
+	node.SetDefaultMTU()
+	node.SetDefaultIsRelayed()
+	node.SetDefaultIsRelay()
+	node.KeyUpdateTimeStamp = time.Now().Unix()
+}
+
+// GetRecordKey - get record key
+func GetRecordKey(id string, network string) (string, error) {
+	if id == "" || network == "" {
+		return "", errors.New("unable to get record key")
+	}
+	return id + "###" + network, nil
+}
+
+// GetNodeByMacAddress - gets a node by mac address
+func GetNodeByMacAddress(network string, macaddress string) (models.Node, error) {
+
+	var node models.Node
+
+	key, err := GetRecordKey(macaddress, network)
+	if err != nil {
+		return node, err
+	}
+
+	record, err := database.FetchRecord(database.NODES_TABLE_NAME, key)
+	if err != nil {
+		return models.Node{}, err
+	}
+
+	if err = json.Unmarshal([]byte(record), &node); err != nil {
+		return models.Node{}, err
+	}
+
+	SetNodeDefaults(&node)
+
+	return node, nil
+}
+
+// GetDeletedNodeByMacAddress - get a deleted node
+func GetDeletedNodeByMacAddress(network string, macaddress string) (models.Node, error) {
+
+	var node models.Node
+
+	key, err := GetRecordKey(macaddress, network)
+	if err != nil {
+		return node, err
+	}
+
+	record, err := database.FetchRecord(database.DELETED_NODES_TABLE_NAME, key)
+	if err != nil {
+		return models.Node{}, err
+	}
+
+	if err = json.Unmarshal([]byte(record), &node); err != nil {
+		return models.Node{}, err
+	}
+
+	SetNodeDefaults(&node)
+
+	return node, nil
+}
+
+// GetNodeRelay - gets the relay node of a given network
+func GetNodeRelay(network string, relayedNodeAddr string) (models.Node, error) {
+	collection, err := database.FetchRecords(database.NODES_TABLE_NAME)
+	var relay models.Node
+	if err != nil {
+		if database.IsEmptyRecord(err) {
+			return relay, nil
+		}
+		Log(err.Error(), 2)
+		return relay, err
+	}
+	for _, value := range collection {
+		err := json.Unmarshal([]byte(value), &relay)
+		if err != nil {
+			Log(err.Error(), 2)
+			continue
+		}
+		if relay.IsRelay == "yes" {
+			for _, addr := range relay.RelayAddrs {
+				if addr == relayedNodeAddr {
+					return relay, nil
+				}
+			}
+		}
+	}
+	return relay, errors.New("could not find relay for node " + relayedNodeAddr)
+}

+ 19 - 29
logic/server.go

@@ -2,6 +2,7 @@ package logic
 
 import (
 	"errors"
+	"fmt"
 	"net"
 	"os"
 	"runtime"
@@ -38,7 +39,7 @@ func ServerJoin(network string, serverID string, privateKey string) error {
 		MacAddress:   serverID,
 		UDPHolePunch: "no",
 	}
-	node.SetDefaults()
+	SetNodeDefaults(node)
 
 	if servercfg.GetPlatform() == "Kubernetes" {
 		node.ListenPort = KUBERNETES_LISTEN_PORT
@@ -108,6 +109,8 @@ func ServerJoin(network string, serverID string, privateKey string) error {
 	node.ListenPort, err = ncutils.GetFreePort(node.ListenPort)
 	if err != nil {
 		Log("Error retrieving port: "+err.Error(), 2)
+	} else {
+		Log("Set client port to "+fmt.Sprintf("%d", node.ListenPort)+" for network "+node.Network, 1)
 	}
 
 	// safety check. If returned node from server is local, but not currently configured as local, set to local addr
@@ -123,7 +126,7 @@ func ServerJoin(network string, serverID string, privateKey string) error {
 	if err = StorePrivKey(node.ID, privateKey); err != nil {
 		return err
 	}
-	if err = ServerPush(node.MacAddress, node.Network); err != nil {
+	if err = ServerPush(node); err != nil {
 		return err
 	}
 
@@ -151,7 +154,7 @@ func ServerCheckin(mac string, network string) error {
 		return err
 	}
 
-	newNode, err = ServerPull(mac, network, false)
+	newNode, err = ServerPull(&serverNode, false)
 	if isDeleteError(err) {
 		return ServerLeave(mac, network)
 	} else if err != nil {
@@ -163,22 +166,16 @@ func ServerCheckin(mac string, network string) error {
 		return errors.New("node has been removed")
 	}
 
-	return ServerPush(newNode.MacAddress, newNode.Network)
+	return ServerPush(newNode)
 }
 
 // ServerPull - pulls current config/peers for server
-func ServerPull(mac string, network string, onErr bool) (*models.Node, error) {
+func ServerPull(serverNode *models.Node, onErr bool) (*models.Node, error) {
 
-	var serverNode models.Node
 	var err error
-	serverNode, err = GetNode(mac, network)
-	if err != nil {
-		return &serverNode, err
-	}
-
 	if serverNode.IPForwarding == "yes" {
 		if err = setIPForwardingLinux(); err != nil {
-			return &serverNode, err
+			return serverNode, err
 		}
 	}
 	serverNode.OS = runtime.GOOS
@@ -196,38 +193,31 @@ func ServerPull(mac string, network string, onErr bool) (*models.Node, error) {
 			Log("removed old interface "+oldIfaceName, 1)
 		}
 		serverNode.PullChanges = "no"
-		if err = setWGConfig(serverNode, network, false); err != nil {
-			return &serverNode, err
+		if err = setWGConfig(*serverNode, serverNode.Network, false); err != nil {
+			return serverNode, err
 		}
 		// handle server side update
-		if err = serverNode.Update(&serverNode); err != nil {
-			return &serverNode, err
+		if err = UpdateNode(serverNode, serverNode); err != nil {
+			return serverNode, err
 		}
 	} else {
-		if err = setWGConfig(serverNode, network, true); err != nil {
+		if err = setWGConfig(*serverNode, serverNode.Network, true); err != nil {
 			if errors.Is(err, os.ErrNotExist) {
-				return ServerPull(serverNode.MacAddress, serverNode.Network, true)
+				return ServerPull(serverNode, true)
 			} else {
-				return &serverNode, err
+				return serverNode, err
 			}
 		}
 	}
 
-	return &serverNode, nil
+	return serverNode, nil
 }
 
 // ServerPush - pushes config changes for server checkins/join
-func ServerPush(mac string, network string) error {
-
-	var serverNode models.Node
-	var err error
-	serverNode, err = GetNode(mac, network)
-	if err != nil /* && !ncutils.IsEmptyRecord(err) May not be necessary */ {
-		return err
-	}
+func ServerPush(serverNode *models.Node) error {
 	serverNode.OS = runtime.GOOS
 	serverNode.SetLastCheckIn()
-	return serverNode.Update(&serverNode)
+	return UpdateNode(serverNode, serverNode)
 }
 
 // ServerLeave - removes a server node

+ 22 - 0
logic/users.go

@@ -0,0 +1,22 @@
+package logic
+
+import (
+	"encoding/json"
+
+	"github.com/gravitl/netmaker/database"
+	"github.com/gravitl/netmaker/models"
+)
+
+// GetUser - gets a user
+func GetUser(username string) (models.User, error) {
+
+	var user models.User
+	record, err := database.FetchRecord(database.USERS_TABLE_NAME, username)
+	if err != nil {
+		return user, err
+	}
+	if err = json.Unmarshal([]byte(record), &user); err != nil {
+		return models.User{}, err
+	}
+	return user, err
+}

+ 32 - 22
logic/util.go

@@ -5,16 +5,14 @@ import (
 	"encoding/base64"
 	"encoding/json"
 	"log"
+	"math/rand"
 	"strconv"
 	"strings"
 	"time"
 
 	"github.com/gravitl/netmaker/database"
-	"github.com/gravitl/netmaker/dnslogic"
-	"github.com/gravitl/netmaker/functions"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/netclient/ncutils"
-	"github.com/gravitl/netmaker/relay"
 	"github.com/gravitl/netmaker/servercfg"
 	"golang.org/x/crypto/bcrypt"
 )
@@ -36,11 +34,10 @@ func CheckEndpoint(endpoint string) bool {
 func SetNetworkServerPeers(node *models.Node) {
 	if currentPeersList, err := GetSystemPeers(node); err == nil {
 		if database.SetPeers(currentPeersList, node.Network) {
-			functions.PrintUserLog(models.NODE_SERVER_NAME, "set new peers on network "+node.Network, 1)
+			Log("set new peers on network "+node.Network, 1)
 		}
 	} else {
-		functions.PrintUserLog(models.NODE_SERVER_NAME, "could not set peers on network "+node.Network, 1)
-		functions.PrintUserLog(models.NODE_SERVER_NAME, err.Error(), 1)
+		Log("could not set peers on network "+node.Network+"\n"+err.Error(), 1)
 	}
 }
 
@@ -73,7 +70,7 @@ func DeleteNode(node *models.Node, exterminate bool) error {
 		return err
 	}
 	if servercfg.IsDNSMode() {
-		err = dnslogic.SetDNS()
+		err = SetDNS()
 	}
 	return removeLocalServer(node)
 }
@@ -101,26 +98,26 @@ func CreateNode(node models.Node, networkName string) (models.Node, error) {
 			node.DNSOn = "no"
 		}
 	}
-	node.SetDefaults()
-	node.Address, err = functions.UniqueAddress(networkName)
+	SetNodeDefaults(&node)
+	node.Address, err = UniqueAddress(networkName)
 	if err != nil {
 		return node, err
 	}
-	node.Address6, err = functions.UniqueAddress6(networkName)
+	node.Address6, err = UniqueAddress6(networkName)
 	if err != nil {
 		return node, err
 	}
 	//Create a JWT for the node
-	tokenString, _ := functions.CreateJWT(node.MacAddress, networkName)
+	tokenString, _ := CreateJWT(node.MacAddress, networkName)
 	if tokenString == "" {
 		//returnErrorResponse(w, r, errorResponse)
 		return node, err
 	}
-	err = node.Validate(false)
+	err = ValidateNode(&node, false)
 	if err != nil {
 		return node, err
 	}
-	key, err := functions.GetRecordKey(node.MacAddress, node.Network)
+	key, err := GetRecordKey(node.MacAddress, node.Network)
 	if err != nil {
 		return node, err
 	}
@@ -133,11 +130,11 @@ func CreateNode(node models.Node, networkName string) (models.Node, error) {
 		return node, err
 	}
 	if node.IsPending != "yes" {
-		functions.DecrimentKey(node.Network, node.AccessKey)
+		DecrimentKey(node.Network, node.AccessKey)
 	}
 	SetNetworkNodesLastModified(node.Network)
 	if servercfg.IsDNSMode() {
-		err = dnslogic.SetDNS()
+		err = SetDNS()
 	}
 	return node, err
 }
@@ -147,7 +144,7 @@ func SetNetworkNodesLastModified(networkName string) error {
 
 	timestamp := time.Now().Unix()
 
-	network, err := functions.GetParentNetwork(networkName)
+	network, err := GetParentNetwork(networkName)
 	if err != nil {
 		return err
 	}
@@ -167,7 +164,7 @@ func SetNetworkNodesLastModified(networkName string) error {
 func GetNode(macaddress string, network string) (models.Node, error) {
 	var node models.Node
 
-	key, err := functions.GetRecordKey(macaddress, network)
+	key, err := GetRecordKey(macaddress, network)
 	if err != nil {
 		return node, err
 	}
@@ -182,7 +179,7 @@ func GetNode(macaddress string, network string) (models.Node, error) {
 	if err = json.Unmarshal([]byte(data), &node); err != nil {
 		return node, err
 	}
-	node.SetDefaults()
+	SetNodeDefaults(&node)
 
 	return node, err
 }
@@ -230,7 +227,7 @@ func GetNodePeers(networkName string, excludeRelayed bool) ([]models.Node, error
 				}
 			}
 			if node.IsRelay == "yes" {
-				network, err := models.GetNetwork(networkName)
+				network, err := GetNetwork(networkName)
 				if err == nil {
 					peer.AllowedIPs = append(peer.AllowedIPs, network.AddressRange)
 				} else {
@@ -253,10 +250,10 @@ func GetPeersList(networkName string, excludeRelayed bool, relayedNodeAddr strin
 		peers, err = GetNodePeers(networkName, excludeRelayed)
 
 	} else {
-		relayNode, err = relay.GetNodeRelay(networkName, relayedNodeAddr)
+		relayNode, err = GetNodeRelay(networkName, relayedNodeAddr)
 		if relayNode.Address != "" {
 			relayNode = setPeerInfo(relayNode)
-			network, err := models.GetNetwork(networkName)
+			network, err := GetNetwork(networkName)
 			if err == nil {
 				relayNode.AllowedIPs = append(relayNode.AllowedIPs, network.AddressRange)
 			} else {
@@ -278,6 +275,19 @@ func GetPeersList(networkName string, excludeRelayed bool, relayedNodeAddr strin
 	return peers, err
 }
 
+// RandomString - returns a random string in a charset
+func RandomString(length int) string {
+	const charset = "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
+
+	var seededRand *rand.Rand = rand.New(rand.NewSource(time.Now().UnixNano()))
+
+	b := make([]byte, length)
+	for i := range b {
+		b[i] = charset[seededRand.Intn(len(charset))]
+	}
+	return string(b)
+}
+
 func setPeerInfo(node models.Node) models.Node {
 	var peer models.Node
 	peer.RelayAddrs = node.RelayAddrs
@@ -303,7 +313,7 @@ func setPeerInfo(node models.Node) models.Node {
 
 func Log(message string, loglevel int) {
 	log.SetFlags(log.Flags() &^ (log.Llongfile | log.Lshortfile))
-	if int32(loglevel) <= servercfg.GetVerbose() && servercfg.GetVerbose() != 0 {
+	if int32(loglevel) <= servercfg.GetVerbose() && servercfg.GetVerbose() >= 0 {
 		log.Println("[netmaker] " + message)
 	}
 }

+ 5 - 6
logic/wireguard.go

@@ -62,10 +62,11 @@ func setWGConfig(node models.Node, network string, peerupdate bool) error {
 		var iface string
 		iface = node.Interface
 		err = setServerPeers(iface, node.PersistentKeepalive, peers)
+		Log("updated peers on server "+node.Name, 2)
 	} else {
 		err = initWireguard(&node, privkey, peers, hasGateway, gateways)
+		Log("finished setting wg config on server "+node.Name, 3)
 	}
-	Log("finished setting wg config on server "+node.Name, 1)
 	return err
 }
 
@@ -98,8 +99,7 @@ func initWireguard(node *models.Node, privkey string, peers []wgtypes.PeerConfig
 	}
 
 	nodeport := int(node.ListenPort)
-	var conf wgtypes.Config
-	conf = wgtypes.Config{
+	var conf = wgtypes.Config{
 		PrivateKey:   &key,
 		ListenPort:   &nodeport,
 		ReplacePeers: true,
@@ -117,8 +117,7 @@ func initWireguard(node *models.Node, privkey string, peers []wgtypes.PeerConfig
 			return err
 		}
 		// spin up userspace + apply the conf file
-		var deviceiface string
-		deviceiface = ifacename
+		var deviceiface = ifacename
 		d, _ := wgclient.Device(deviceiface)
 		for d != nil && d.Name == deviceiface {
 			_ = RemoveConf(ifacename, false) // remove interface first
@@ -168,7 +167,7 @@ func initWireguard(node *models.Node, privkey string, peers []wgtypes.PeerConfig
 		}
 		// set MTU of node interface
 		if _, err := ncutils.RunCmd(ipExec+" link set mtu "+strconv.Itoa(int(node.MTU))+" up dev "+ifacename, true); err != nil {
-			Log("failed to create interface with mtu "+ifacename, 2)
+			Log("failed to create interface with mtu "+strconv.Itoa(int(node.MTU))+" - "+ifacename, 2)
 			return err
 		}
 

+ 26 - 16
main.go

@@ -13,11 +13,12 @@ import (
 	"sync"
 	"time"
 
+	"github.com/gravitl/netmaker/auth"
 	controller "github.com/gravitl/netmaker/controllers"
 	"github.com/gravitl/netmaker/database"
-	"github.com/gravitl/netmaker/dnslogic"
 	"github.com/gravitl/netmaker/functions"
 	nodepb "github.com/gravitl/netmaker/grpc"
+	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/netclient/ncutils"
 	"github.com/gravitl/netmaker/servercfg"
@@ -35,20 +36,29 @@ func main() {
 
 func initialize() { // Client Mode Prereq Check
 	var err error
+
 	if err = database.InitializeDatabase(); err != nil {
-		log.Println("Error connecting to database.")
+		logic.Log("Error connecting to database", 0)
 		log.Fatal(err)
 	}
-	log.Println("database successfully connected.")
+	logic.Log("database successfully connected", 0)
+
+	var authProvider = auth.InitializeAuthProvider()
+	if authProvider != "" {
+		logic.Log("OAuth provider, "+authProvider+", initialized", 0)
+	} else {
+		logic.Log("no OAuth provider found or not configured, continuing without OAuth", 0)
+	}
+
 	if servercfg.IsClientMode() != "off" {
 		output, err := ncutils.RunCmd("id -u", true)
 		if err != nil {
-			log.Println("Error running 'id -u' for prereq check. Please investigate or disable client mode.")
+			logic.Log("Error running 'id -u' for prereq check. Please investigate or disable client mode.", 0)
 			log.Fatal(output, err)
 		}
 		uid, err := strconv.Atoi(string(output[:len(output)-1]))
 		if err != nil {
-			log.Println("Error retrieving uid from 'id -u' for prereq check. Please investigate or disable client mode.")
+			logic.Log("Error retrieving uid from 'id -u' for prereq check. Please investigate or disable client mode.", 0)
 			log.Fatal(err)
 		}
 		if uid != 0 {
@@ -74,7 +84,7 @@ func startControllers() {
 		if !(servercfg.DisableRemoteIPCheck()) && servercfg.GetGRPCHost() == "127.0.0.1" {
 			err := servercfg.SetHost()
 			if err != nil {
-				log.Println("Unable to Set host. Exiting...")
+				logic.Log("Unable to Set host. Exiting...", 0)
 				log.Fatal(err)
 			}
 		}
@@ -88,9 +98,9 @@ func startControllers() {
 	}
 
 	if servercfg.IsDNSMode() {
-		err := dnslogic.SetDNS()
+		err := logic.SetDNS()
 		if err != nil {
-			log.Println("error occurred initializing DNS:", err)
+			logic.Log("error occurred initializing DNS: "+err.Error(), 0)
 		}
 	}
 	//Run Rest Server
@@ -98,7 +108,7 @@ func startControllers() {
 		if !servercfg.DisableRemoteIPCheck() && servercfg.GetAPIHost() == "127.0.0.1" {
 			err := servercfg.SetHost()
 			if err != nil {
-				log.Println("Unable to Set host. Exiting...")
+				logic.Log("Unable to Set host. Exiting...", 0)
 				log.Fatal(err)
 			}
 		}
@@ -106,11 +116,11 @@ func startControllers() {
 		controller.HandleRESTRequests(&waitnetwork)
 	}
 	if !servercfg.IsAgentBackend() && !servercfg.IsRestBackend() {
-		log.Println("No Server Mode selected, so nothing is being served! Set either Agent mode (AGENT_BACKEND) or Rest mode (REST_BACKEND) to 'true'.")
+		logic.Log("No Server Mode selected, so nothing is being served! Set either Agent mode (AGENT_BACKEND) or Rest mode (REST_BACKEND) to 'true'.", 0)
 	}
 
 	waitnetwork.Wait()
-	log.Println("[netmaker] exiting")
+	logic.Log("exiting", 0)
 }
 
 func runClient(wg *sync.WaitGroup) {
@@ -139,7 +149,7 @@ func runGRPC(wg *sync.WaitGroup) {
 	listener, err := net.Listen("tcp", ":"+grpcport)
 	// Handle errors if any
 	if err != nil {
-		log.Fatalf("Unable to listen on port "+grpcport+", error: %v", err)
+		log.Fatalf("[netmaker] Unable to listen on port "+grpcport+", error: %v", err)
 	}
 
 	s := grpc.NewServer(
@@ -157,7 +167,7 @@ func runGRPC(wg *sync.WaitGroup) {
 			log.Fatalf("Failed to serve: %v", err)
 		}
 	}()
-	log.Println("Agent Server successfully started on port " + grpcport + " (gRPC)")
+	logic.Log("Agent Server successfully started on port "+grpcport+" (gRPC)", 0)
 
 	// Right way to stop the server using a SHUTDOWN HOOK
 	// Create a channel to receive OS signals
@@ -172,11 +182,11 @@ func runGRPC(wg *sync.WaitGroup) {
 	<-c
 
 	// After receiving CTRL+C Properly stop the server
-	log.Println("Stopping the Agent server...")
+	logic.Log("Stopping the Agent server...", 0)
 	s.Stop()
 	listener.Close()
-	log.Println("Agent server closed..")
-	log.Println("Closed DB connection.")
+	logic.Log("Agent server closed..", 0)
+	logic.Log("Closed DB connection.", 0)
 }
 
 func authServerUnaryInterceptor() grpc.ServerOption {

+ 0 - 32
models/extclient.go

@@ -1,11 +1,5 @@
 package models
 
-import (
-	"encoding/json"
-
-	"github.com/gravitl/netmaker/database"
-)
-
 // ExtClient - struct for external clients
 type ExtClient struct {
 	ClientID               string `json:"clientid" bson:"clientid"`
@@ -18,29 +12,3 @@ type ExtClient struct {
 	IngressGatewayEndpoint string `json:"ingressgatewayendpoint" bson:"ingressgatewayendpoint"`
 	LastModified           int64  `json:"lastmodified" bson:"lastmodified"`
 }
-
-// ExtClient.GetEgressRangesOnNetwork - returns the egress ranges on network of ext client
-func (client *ExtClient) GetEgressRangesOnNetwork() ([]string, error) {
-
-	var result []string
-	nodesData, err := database.FetchRecords(database.NODES_TABLE_NAME)
-	if err != nil {
-		return []string{}, err
-	}
-	for _, nodeData := range nodesData {
-		var currentNode Node
-		if err = json.Unmarshal([]byte(nodeData), &currentNode); err != nil {
-			continue
-		}
-		if currentNode.Network != client.Network {
-			continue
-		}
-		if currentNode.IsEgressGateway == "yes" { // add the egress gateway range(s) to the result
-			if len(currentNode.EgressGatewayRanges) > 0 {
-				result = append(result, currentNode.EgressGatewayRanges...)
-			}
-		}
-	}
-
-	return result, nil
-}

+ 0 - 166
models/network.go

@@ -1,14 +1,9 @@
 package models
 
 import (
-	"encoding/json"
-	"errors"
-	"fmt"
 	"strings"
 	"time"
 
-	"github.com/go-playground/validator/v10"
-	"github.com/gravitl/netmaker/database"
 	"github.com/gravitl/netmaker/servercfg"
 )
 
@@ -50,19 +45,6 @@ type SaveData struct { // put sensitive fields here
 	NetID string `json:"netid" bson:"netid" validate:"required,min=1,max=12,netid_valid"`
 }
 
-// Network.NetIDInNetworkCharSet - checks if a netid of a network uses valid characters
-func (network *Network) NetIDInNetworkCharSet() bool {
-
-	charset := "abcdefghijklmnopqrstuvwxyz1234567890-_."
-
-	for _, char := range network.NetID {
-		if !strings.Contains(charset, strings.ToLower(string(char))) {
-			return false
-		}
-	}
-	return true
-}
-
 // Network.DisplayNameInNetworkCharSet - checks if displayname uses valid characters
 func (network *Network) DisplayNameInNetworkCharSet() bool {
 
@@ -76,103 +58,6 @@ func (network *Network) DisplayNameInNetworkCharSet() bool {
 	return true
 }
 
-// GetNetworks - returns all networks from database
-func GetNetworks() ([]Network, error) {
-	var networks []Network
-
-	collection, err := database.FetchRecords(database.NETWORKS_TABLE_NAME)
-
-	if err != nil {
-		return networks, err
-	}
-
-	for _, value := range collection {
-		var network Network
-		if err := json.Unmarshal([]byte(value), &network); err != nil {
-			return networks, err
-		}
-		// add network our array
-		networks = append(networks, network)
-	}
-
-	return networks, err
-}
-
-// Network.IsNetworkDisplayNameUnique - checks if displayname is unique from other networks
-func (network *Network) IsNetworkDisplayNameUnique() (bool, error) {
-
-	isunique := true
-
-	records, err := GetNetworks()
-
-	if err != nil && !database.IsEmptyRecord(err) {
-		return false, err
-	}
-
-	for i := 0; i < len(records); i++ {
-
-		if network.NetID == records[i].DisplayName {
-			isunique = false
-		}
-	}
-
-	return isunique, nil
-}
-
-// Network.IsNetworkNameUnique - checks to see if any other networks have the same name (id)
-func (network *Network) IsNetworkNameUnique() (bool, error) {
-
-	isunique := true
-
-	dbs, err := GetNetworks()
-
-	if err != nil && !database.IsEmptyRecord(err) {
-		return false, err
-	}
-
-	for i := 0; i < len(dbs); i++ {
-
-		if network.NetID == dbs[i].NetID {
-			isunique = false
-		}
-	}
-
-	return isunique, nil
-}
-
-// Network.Validate - validates fields of an network struct
-func (network *Network) Validate(isUpdate bool) error {
-	v := validator.New()
-	_ = v.RegisterValidation("netid_valid", func(fl validator.FieldLevel) bool {
-		inCharSet := network.NetIDInNetworkCharSet()
-		if isUpdate {
-			return inCharSet
-		}
-		isFieldUnique, _ := network.IsNetworkNameUnique()
-		return isFieldUnique && inCharSet
-	})
-	//
-	_ = v.RegisterValidation("displayname_valid", func(fl validator.FieldLevel) bool {
-		isFieldUnique, _ := network.IsNetworkDisplayNameUnique()
-		inCharSet := network.DisplayNameInNetworkCharSet()
-		if isUpdate {
-			return inCharSet
-		}
-		return isFieldUnique && inCharSet
-	})
-	_ = v.RegisterValidation("checkyesorno", func(fl validator.FieldLevel) bool {
-		return CheckYesOrNo(fl)
-	})
-	err := v.Struct(network)
-	if err != nil {
-		for _, e := range err.(validator.ValidationErrors) {
-			fmt.Println(e)
-		}
-	}
-
-	return err
-}
-
 // Network.SetNodesLastModified - sets nodes last modified on network, depricated
 func (network *Network) SetNodesLastModified() {
 	network.NodesLastModified = time.Now().Unix()
@@ -242,54 +127,3 @@ func (network *Network) SetDefaults() {
 		network.DefaultMTU = 1280
 	}
 }
-
-// Network.Update - updates a network with another network's fields
-func (currentNetwork *Network) Update(newNetwork *Network) (bool, bool, error) {
-	if err := newNetwork.Validate(true); err != nil {
-		return false, false, err
-	}
-	if newNetwork.NetID == currentNetwork.NetID {
-		hasrangeupdate := newNetwork.AddressRange != currentNetwork.AddressRange
-		localrangeupdate := newNetwork.LocalRange != currentNetwork.LocalRange
-		data, err := json.Marshal(newNetwork)
-		if err != nil {
-			return false, false, err
-		}
-		newNetwork.SetNetworkLastModified()
-		err = database.Insert(newNetwork.NetID, string(data), database.NETWORKS_TABLE_NAME)
-		return hasrangeupdate, localrangeupdate, err
-	}
-	// copy values
-	return false, false, errors.New("failed to update network " + newNetwork.NetID + ", cannot change netid.")
-}
-
-// Network.SetNetworkNodesLastModified - sets network nodes last modified time
-func (network *Network) SetNetworkNodesLastModified() error {
-
-	timestamp := time.Now().Unix()
-
-	network.NodesLastModified = timestamp
-	data, err := json.Marshal(&network)
-	if err != nil {
-		return err
-	}
-	err = database.Insert(network.NetID, string(data), database.NETWORKS_TABLE_NAME)
-	if err != nil {
-		return err
-	}
-	return nil
-}
-
-// GetNetwork - gets a network from database
-func GetNetwork(networkname string) (Network, error) {
-
-	var network Network
-	networkData, err := database.FetchRecord(database.NETWORKS_TABLE_NAME, networkname)
-	if err != nil {
-		return network, err
-	}
-	if err = json.Unmarshal([]byte(networkData), &network); err != nil {
-		return Network{}, err
-	}
-	return network, nil
-}

+ 0 - 172
models/node.go

@@ -2,15 +2,12 @@ package models
 
 import (
 	"bytes"
-	"encoding/json"
 	"errors"
 	"math/rand"
 	"net"
 	"strings"
 	"time"
 
-	"github.com/go-playground/validator/v10"
-	"github.com/gravitl/netmaker/database"
 	"golang.org/x/crypto/bcrypt"
 )
 
@@ -207,105 +204,6 @@ func (node *Node) SetDefaultName() {
 	}
 }
 
-func (node *Node) CheckIsServer() bool {
-	nodeData, err := database.FetchRecords(database.NODES_TABLE_NAME)
-	if err != nil && !database.IsEmptyRecord(err) {
-		return false
-	}
-	for _, value := range nodeData {
-		var tmpNode Node
-		if err := json.Unmarshal([]byte(value), &tmpNode); err != nil {
-			continue
-		}
-		if tmpNode.Network == node.Network && tmpNode.MacAddress != node.MacAddress {
-			return false
-		}
-	}
-	return true
-}
-
-func (node *Node) GetNetwork() (Network, error) {
-
-	var network Network
-	networkData, err := database.FetchRecord(database.NETWORKS_TABLE_NAME, node.Network)
-	if err != nil {
-		return network, err
-	}
-	if err = json.Unmarshal([]byte(networkData), &network); err != nil {
-		return Network{}, err
-	}
-	return network, nil
-}
-
-//TODO: I dont know why this exists
-//This should exist on the node.go struct. I'm sure there was a reason?
-func (node *Node) SetDefaults() {
-
-	//TODO: Maybe I should make Network a part of the node struct. Then we can just query the Network object for stuff.
-	parentNetwork, _ := node.GetNetwork()
-
-	node.ExpirationDateTime = time.Now().Unix() + TEN_YEARS_IN_SECONDS
-
-	if node.ListenPort == 0 {
-		node.ListenPort = parentNetwork.DefaultListenPort
-	}
-	if node.SaveConfig == "" {
-		if parentNetwork.DefaultSaveConfig != "" {
-			node.SaveConfig = parentNetwork.DefaultSaveConfig
-		} else {
-			node.SaveConfig = "yes"
-		}
-	}
-	if node.Interface == "" {
-		node.Interface = parentNetwork.DefaultInterface
-	}
-	if node.PersistentKeepalive == 0 {
-		node.PersistentKeepalive = parentNetwork.DefaultKeepalive
-	}
-	if node.PostUp == "" {
-		postup := parentNetwork.DefaultPostUp
-		node.PostUp = postup
-	}
-	if node.IsStatic == "" {
-		node.IsStatic = "no"
-	}
-	if node.UDPHolePunch == "" {
-		node.UDPHolePunch = parentNetwork.DefaultUDPHolePunch
-		if node.UDPHolePunch == "" {
-			node.UDPHolePunch = "yes"
-		}
-	}
-	// == Parent Network settings ==
-	if node.IsDualStack == "" {
-		node.IsDualStack = parentNetwork.IsDualStack
-	}
-	if node.MTU == 0 {
-		node.MTU = parentNetwork.DefaultMTU
-	}
-	// == node defaults if not set by parent ==
-	node.SetIPForwardingDefault()
-	node.SetDNSOnDefault()
-	node.SetIsLocalDefault()
-	node.SetIsDualStackDefault()
-	node.SetLastModified()
-	node.SetDefaultName()
-	node.SetLastCheckIn()
-	node.SetLastPeerUpdate()
-	node.SetRoamingDefault()
-	node.SetPullChangesDefault()
-	node.SetDefaultAction()
-	node.SetID()
-	node.SetIsServerDefault()
-	node.SetIsStaticDefault()
-	node.SetDefaultEgressGateway()
-	node.SetDefaultIngressGateway()
-	node.SetDefaulIsPending()
-	node.SetDefaultMTU()
-	node.SetDefaultIsRelayed()
-	node.SetDefaultIsRelay()
-	node.KeyUpdateTimeStamp = time.Now().Unix()
-}
-
 func (newNode *Node) Fill(currentNode *Node) {
 	if newNode.ID == "" {
 		newNode.ID = currentNode.ID
@@ -454,23 +352,6 @@ func (newNode *Node) Fill(currentNode *Node) {
 	}
 }
 
-func (currentNode *Node) Update(newNode *Node) error {
-	newNode.Fill(currentNode)
-	if err := newNode.Validate(true); err != nil {
-		return err
-	}
-	newNode.SetID()
-	if newNode.ID == currentNode.ID {
-		newNode.SetLastModified()
-		if data, err := json.Marshal(newNode); err != nil {
-			return err
-		} else {
-			return database.Insert(newNode.ID, string(data), database.NODES_TABLE_NAME)
-		}
-	}
-	return errors.New("failed to update node " + newNode.MacAddress + ", cannot change macaddress.")
-}
-
 func StringWithCharset(length int, charset string) string {
 	b := make([]byte, length)
 	for i := range b {
@@ -486,36 +367,6 @@ func IsIpv4Net(host string) bool {
 	return net.ParseIP(host) != nil
 }
 
-func (node *Node) Validate(isUpdate bool) error {
-	v := validator.New()
-	_ = v.RegisterValidation("macaddress_unique", func(fl validator.FieldLevel) bool {
-		if isUpdate {
-			return true
-		}
-		isFieldUnique, _ := node.IsIDUnique()
-		return isFieldUnique
-	})
-	_ = v.RegisterValidation("network_exists", func(fl validator.FieldLevel) bool {
-		_, err := node.GetNetwork()
-		return err == nil
-	})
-	_ = v.RegisterValidation("in_charset", func(fl validator.FieldLevel) bool {
-		isgood := node.NameInNodeCharSet()
-		return isgood
-	})
-	_ = v.RegisterValidation("checkyesorno", func(fl validator.FieldLevel) bool {
-		return CheckYesOrNo(fl)
-	})
-	err := v.Struct(node)
-
-	return err
-}
-
-func (node *Node) IsIDUnique() (bool, error) {
-	_, err := database.FetchRecord(database.NODES_TABLE_NAME, node.ID)
-	return database.IsEmptyRecord(err), err
-}
-
 func (node *Node) NameInNodeCharSet() bool {
 
 	charset := "abcdefghijklmnopqrstuvwxyz1234567890-"
@@ -528,29 +379,6 @@ func (node *Node) NameInNodeCharSet() bool {
 	return true
 }
 
-func GetAllNodes() ([]Node, error) {
-	var nodes []Node
-
-	collection, err := database.FetchRecords(database.NODES_TABLE_NAME)
-	if err != nil {
-		if database.IsEmptyRecord(err) {
-			return []Node{}, nil
-		}
-		return []Node{}, err
-	}
-
-	for _, value := range collection {
-		var node Node
-		if err := json.Unmarshal([]byte(value), &node); err != nil {
-			return []Node{}, err
-		}
-		// add node to our array
-		nodes = append(nodes, node)
-	}
-
-	return nodes, nil
-}
-
 func (node *Node) GetID() (string, error) {
 	if node.MacAddress == "" || node.Network == "" {
 		return "", errors.New("unable to get record key")

+ 0 - 4
netclient/functions/checkin.go

@@ -230,10 +230,6 @@ func Pull(network string, manual bool) (*models.Node, error) {
 			if err != nil {
 				return &resNode, err
 			}
-		} else { // handle server side update
-			if err = resNode.Update(&resNode); err != nil {
-				return &resNode, err
-			}
 		}
 	} else {
 		if err = wireguard.SetWGConfig(network, true); err != nil {

+ 4 - 4
netclient/functions/join.go

@@ -5,8 +5,6 @@ import (
 	"encoding/json"
 	"errors"
 	"fmt"
-	"log"
-	"os/exec"
 	nodepb "github.com/gravitl/netmaker/grpc"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/netclient/auth"
@@ -18,6 +16,8 @@ import (
 	"github.com/gravitl/netmaker/netclient/wireguard"
 	"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
 	"google.golang.org/grpc"
+	"log"
+	"os/exec"
 )
 
 // JoinNetwork - helps a client join a network
@@ -84,8 +84,8 @@ func JoinNetwork(cfg config.ClientConfig, privateKey string) error {
 	if ncutils.IsLinux() {
 		_, err := exec.LookPath("resolvectl")
 		if err != nil {
-			ncutils.PrintLog("resolvectl not present",2)
-			ncutils.PrintLog("unable to configure DNS automatically, disabling automated DNS management",2)
+			ncutils.PrintLog("resolvectl not present", 2)
+			ncutils.PrintLog("unable to configure DNS automatically, disabling automated DNS management", 2)
 			cfg.Node.DNSOn = "no"
 		}
 	}

+ 1 - 1
netclient/main.go

@@ -24,7 +24,7 @@ func main() {
 	app := cli.NewApp()
 	app.Name = "Netclient CLI"
 	app.Usage = "Netmaker's netclient agent and CLI. Used to perform interactions with Netmaker server and set local WireGuard config."
-	app.Version = "v0.8.4"
+	app.Version = "v0.8.5"
 
 	hostname, err := os.Hostname()
 	if err != nil {

+ 9 - 2
netclient/ncutils/netclientutils.go

@@ -155,9 +155,9 @@ func parsePeers(keepalive int32, peers []wgtypes.PeerConfig) (string, error) {
 	if keepalive <= 0 {
 		keepalive = 20
 	}
-	
+
 	for _, peer := range peers {
-		endpointString :=  ""
+		endpointString := ""
 		if peer.Endpoint != nil && peer.Endpoint.String() != "" {
 			endpointString += "Endpoint = " + peer.Endpoint.String()
 		}
@@ -271,6 +271,7 @@ func GetFreePort(rangestart int32) (int32, error) {
 	if err != nil {
 		return 0, err
 	}
+
 	for x := rangestart; x <= 65535; x++ {
 		conflict := false
 		for _, i := range devices {
@@ -397,6 +398,12 @@ func FileExists(f string) bool {
 	if os.IsNotExist(err) {
 		return false
 	}
+	if err != nil && strings.Contains(err.Error(), "not a directory") {
+		return false
+	}
+	if err != nil {
+		Log("error reading file: " + f + ", " + err.Error())
+	}
 	return !info.IsDir()
 }
 

+ 2 - 0
netclient/wireguard/common.go

@@ -107,10 +107,12 @@ func InitWireguard(node *models.Node, privkey string, peers []wgtypes.PeerConfig
 	if err != nil {
 		return err
 	}
+
 	modcfg, err := config.ReadConfig(node.Network)
 	if err != nil {
 		return err
 	}
+
 	nodecfg := modcfg.Node
 	servercfg := modcfg.Server
 

+ 0 - 38
relay/relay.go

@@ -1,38 +0,0 @@
-package relay
-
-import (
-	"encoding/json"
-	"errors"
-
-	"github.com/gravitl/netmaker/database"
-	"github.com/gravitl/netmaker/functions"
-	"github.com/gravitl/netmaker/models"
-)
-
-// GetNodeRelay - gets the relay node of a given network
-func GetNodeRelay(network string, relayedNodeAddr string) (models.Node, error) {
-	collection, err := database.FetchRecords(database.NODES_TABLE_NAME)
-	var relay models.Node
-	if err != nil {
-		if database.IsEmptyRecord(err) {
-			return relay, nil
-		}
-		functions.PrintUserLog("", err.Error(), 2)
-		return relay, err
-	}
-	for _, value := range collection {
-		err := json.Unmarshal([]byte(value), &relay)
-		if err != nil {
-			functions.PrintUserLog("", err.Error(), 2)
-			continue
-		}
-		if relay.IsRelay == "yes" {
-			for _, addr := range relay.RelayAddrs {
-				if addr == relayedNodeAddr {
-					return relay, nil
-				}
-			}
-		}
-	}
-	return relay, errors.New("could not find relay for node " + relayedNodeAddr)
-}

+ 80 - 26
scripts/netclient-install.sh

@@ -1,44 +1,98 @@
-#!/bin/bash
-set -e
+#!/bin/sh
 
-if [ "$EUID" -ne 0 ]; then
+if [ $(id -u) -ne 0 ]; then
    echo "This script must be run as root" 
    exit 1
 fi
 
+echo "checking dependencies..."
+
+if [ -f /etc/debian_version ]; then
+	install_cmd='apt-get install -y'
+elif [ -f /etc/alpine-release ]; then
+	install_cmd='apk --update add'
+elif [ -f /etc/centos-release ]; then
+	install_cmd='yum install -y'
+elif [ -f /etc/fedora-release ]; then
+	install_cmd='dnf install -y'
+else
+	install_cmd=''
+fi
+
+if [ -z "${install_cmd}" ]; then
+        echo "OS unsupported for automatic dependency install"
+	exit 1
+fi
+dependencies="wireguard resolvectl"
+set -- $dependencies
+while [ -n "$1" ]; do
+    echo $1
+	is_installed=$(dpkg-query -W --showformat='${Status}\n' $1 | grep "install ok installed")
+	if [ "${is_installed}" = "install ok installed" ]; then
+		echo "    " $1 is installed
+	else
+		echo "    " $1 is not installed. Attempting install.
+		${install_cmd} $1
+		sleep 5
+		is_installed=$(dpkg-query -W --showformat='${Status}\n' $1 | grep "install ok installed")
+          	if [ "${is_installed}" = "install ok installed" ]; then
+			echo "    " $1 is installed
+		elif [ -x "$(command -v $1)" ]; then
+			echo "    " $1 is installed
+		else
+			echo "    " FAILED TO INSTALL $1
+			echo "    " This may break functionality.
+		fi
+	fi
+	shift
+done
+
+set -e
+
 [ -z "$KEY" ] && KEY=nokey;
 [ -z "$VERSION" ] && echo "no \$VERSION provided, fallback to latest" && VERSION=latest;
 
 dist=netclient
 
-echo "OS Version = $OSTYPE"
+echo "OS Version = $(uname)"
 echo "Netclient Version = $VERSION"
 
-if [[ "$OSTYPE" == "linux-gnu"* ]]; then
-	arch=$(uname -i)
-	echo "CPU ARCH = $arch"
-	if [ "$arch" == 'x86_64' ];
-	then 
-		dist=netclient 
-	fi
-	if [ "$arch" == 'x86_32' ];
-	then
-		dist=netclient-32
-	fi
-	if [ "$arch" == 'armv*' ];
-	then
-		dist=netclient-arm64
-	fi
-elif [[ "$OSTYPE" == "darwin"* ]]; then
-        dist=netclient-darwin
-else
-        echo "This OS is not currently supported via automated install" 
-        exit 1
-fi
+case $(uname | tr '[:upper:]' '[:lower:]') in
+	linux*)
+		if [ -z "$CPU_ARCH" ]; then
+			CPU_ARCH=$(uname -m)
+		fi
+		case $CPU_ARCH in
+			amd64)
+				dist=netclient
+			;;
+			x86_64)
+				dist=netclient
+			;;
+                        x86_32)
+                                dist=netclient-32
+                        ;;
+ 			arm64)
+				dist=netclient-arm64
+			;;
+			aarch64)
+                                dist=netclient-arm64
+			;;
+			arm*)
+				dist=netclient-$CPU_ARCH
+            		;;
+			*)
+				fatal "$CPU_ARCH : cpu architecture not supported"
+    		esac
+	;;
+	darwin)
+        	dist=netclient-darwin
+	;;
+esac
 
 echo "Binary = $dist"
 
-wget -O netclient https://github.com/gravitl/netmaker/releases/download/$VERSION/netclient
+wget -nv -O netclient https://github.com/gravitl/netmaker/releases/download/$VERSION/$dist
 chmod +x netclient
 sudo ./netclient join -t $KEY
 rm -f netclient

+ 109 - 0
scripts/nm-quick-server.sh

@@ -0,0 +1,109 @@
+#!/bin/bash
+echo "checking for root permissions..."
+
+if [ $EUID -ne 0 ]; then
+   echo "This script must be run as root" 
+   exit 1
+fi
+
+
+echo "checking dependencies..."
+
+declare -A osInfo;
+osInfo[/etc/debian_version]="apt-get install -y"
+osInfo[/etc/alpine-release]="apk --update add"
+osInfo[/etc/centos-release]="yum install -y"
+osInfo[/etc/fedora-release]="dnf install -y"
+
+for f in ${!osInfo[@]}
+do
+    if [[ -f $f ]];then
+        install_cmd=${osInfo[$f]}
+    fi
+done
+
+dependencies=("docker.io" "docker-compose" "wireguard")
+
+for dependency in ${dependencies[@]}; do
+    is_installed=$(dpkg-query -W --showformat='${Status}\n' ${dependency} | grep "install ok installed")
+
+    if [ "${is_installed}" == "install ok installed" ]; then
+        echo "    " ${dependency} is installed
+    else
+            echo "    " ${dependency} is not installed. Attempting install.
+            ${install_cmd} ${dependency}
+            sleep 5
+            is_installed=$(dpkg-query -W --showformat='${Status}\n' ${dependency} | grep "install ok installed")
+            if [ "${is_installed}" == "install ok installed" ]; then
+                echo "    " ${dependency} is installed
+            elif [ -x "$(command -v ${dependency})" ]; then
+                echo "    " ${dependency} is installed
+            else
+                echo "    " failed to install ${dependency}. Exiting.
+                exit 1
+            fi
+    fi
+done
+
+
+
+
+set -e
+
+echo "setting public ip values..."
+
+NETMAKER_BASE_DOMAIN=nm.$(curl -s ifconfig.me | tr . -).nip.io
+COREDNS_IP=$(ip route get 1 | sed -n 's/^.*src \([0-9.]*\) .*$/\1/p')
+SERVER_PUBLIC_IP=$(curl -s ifconfig.me)
+REPLACE_MASTER_KEY=$(tr -dc A-Za-z0-9 </dev/urandom | head -c 30 ; echo '')
+EMAIL="[email protected]"
+
+echo "        domain: $NETMAKER_BASE_DOMAIN"
+echo "    coredns ip: $COREDNS_IP"
+echo "     public ip: $SERVER_PUBLIC_IP"
+echo "    master key: $REPLACE_MASTER_KEY"
+
+
+echo "setting caddyfile..."
+
+
+wget -O /root/Caddyfile https://raw.githubusercontent.com/gravitl/netmaker/master/docker/Caddyfile
+sed -i "s/NETMAKER_BASE_DOMAIN/$NETMAKER_BASE_DOMAIN/g" /root/Caddyfile
+sed -i "s/YOUR_EMAIL/$EMAIL/g" /root/Caddyfile
+
+
+echo "setting docker-compose..."
+
+wget -O /root/docker-compose.yml https://raw.githubusercontent.com/gravitl/netmaker/master/compose/docker-compose.caddy.yml
+sed -i "s/NETMAKER_BASE_DOMAIN/$NETMAKER_BASE_DOMAIN/g" /root/docker-compose.yml
+sed -i "s/SERVER_PUBLIC_IP/$SERVER_PUBLIC_IP/g" /root/docker-compose.yml
+sed -i "s/COREDNS_IP/$COREDNS_IP/g" /root/docker-compose.yml
+sed -i "s/REPLACE_MASTER_KEY/$REPLACE_MASTER_KEY/g" /root/docker-compose.yml
+
+echo "starting containers..."
+
+docker-compose -f /root/docker-compose.yml up -d
+
+cat << "EOF"
+
+
+    ______     ______     ______     __   __   __     ______   __                        
+   /\  ___\   /\  == \   /\  __ \   /\ \ / /  /\ \   /\__  _\ /\ \                       
+   \ \ \__ \  \ \  __<   \ \  __ \  \ \ \'/   \ \ \  \/_/\ \/ \ \ \____                  
+    \ \_____\  \ \_\ \_\  \ \_\ \_\  \ \__|    \ \_\    \ \_\  \ \_____\                 
+     \/_____/   \/_/ /_/   \/_/\/_/   \/_/      \/_/     \/_/   \/_____/                 
+                                                                                         
+ __   __     ______     ______   __    __     ______     __  __     ______     ______    
+/\ "-.\ \   /\  ___\   /\__  _\ /\ "-./  \   /\  __ \   /\ \/ /    /\  ___\   /\  == \   
+\ \ \-.  \  \ \  __\   \/_/\ \/ \ \ \-./\ \  \ \  __ \  \ \  _"-.  \ \  __\   \ \  __<   
+ \ \_\\"\_\  \ \_____\    \ \_\  \ \_\ \ \_\  \ \_\ \_\  \ \_\ \_\  \ \_____\  \ \_\ \_\ 
+  \/_/ \/_/   \/_____/     \/_/   \/_/  \/_/   \/_/\/_/   \/_/\/_/   \/_____/   \/_/ /_/ 
+                                                                                         															 
+
+EOF
+
+echo "             finished installing"
+echo " "
+echo "             visit dashboard.$NETMAKER_BASE_DOMAIN to log in"
+echo " "
+echo " "

+ 81 - 13
scripts/nm-quick.sh

@@ -45,9 +45,6 @@ for dependency in ${dependencies[@]}; do
     fi
 done
 
-
-
-
 set -e
 
 echo "setting public ip values..."
@@ -55,30 +52,54 @@ echo "setting public ip values..."
 NETMAKER_BASE_DOMAIN=nm.$(curl -s ifconfig.me | tr . -).nip.io
 COREDNS_IP=$(ip route get 1 | sed -n 's/^.*src \([0-9.]*\) .*$/\1/p')
 SERVER_PUBLIC_IP=$(curl -s ifconfig.me)
-REPLACE_MASTER_KEY=$(tr -dc A-Za-z0-9 </dev/urandom | head -c 30 ; echo '')
+MASTER_KEY=$(tr -dc A-Za-z0-9 </dev/urandom | head -c 30 ; echo '')
 EMAIL="[email protected]"
 
+arg1=$( echo $1 | awk -F"domain=" '{print $2}')
+arg2=$( echo $2 | awk -F"domain=" '{print $2}')
+
+if [ -n "$arg1" ]; then
+  echo "Parameter NETMAKER_BASE_DOMAIN is $arg1"
+  NETMAKER_BASE_DOMAIN=$arg1
+
+elif [ -n "$arg2" ]; then
+  echo "Parameter NETMAKER_BASE_DOMAIN is $arg2"
+  NETMAKER_BASE_DOMAIN=$arg2
+fi
+
+arg1=$( echo $1 | awk -F"email=" '{print $2}')
+arg2=$( echo $2 | awk -F"email=" '{print $2}')
+
+if [ -n "$arg1" ]; then
+  echo "Parameter EMAIL is $arg1"
+  EMAIL=$arg1
+
+elif [ -n "$arg2" ]; then
+  echo "Parameter EMAIL is $arg2"
+  EMAIL=$arg2
+fi
+
 echo "        domain: $NETMAKER_BASE_DOMAIN"
 echo "    coredns ip: $COREDNS_IP"
 echo "     public ip: $SERVER_PUBLIC_IP"
-echo "    master key: $REPLACE_MASTER_KEY"
+echo "    master key: $MASTER_KEY"
 
 
 echo "setting caddyfile..."
 
 
-wget -O /root/Caddyfile https://raw.githubusercontent.com/gravitl/netmaker/master/docker/Caddyfile
+wget -q -O /root/Caddyfile https://raw.githubusercontent.com/gravitl/netmaker/master/docker/Caddyfile
 sed -i "s/NETMAKER_BASE_DOMAIN/$NETMAKER_BASE_DOMAIN/g" /root/Caddyfile
 sed -i "s/YOUR_EMAIL/$EMAIL/g" /root/Caddyfile
 
 
 echo "setting docker-compose..."
 
-wget -O /root/docker-compose.yml https://raw.githubusercontent.com/gravitl/netmaker/master/compose/docker-compose.caddy.yml
+wget -q -O /root/docker-compose.yml https://raw.githubusercontent.com/gravitl/netmaker/master/compose/docker-compose.caddy.yml
 sed -i "s/NETMAKER_BASE_DOMAIN/$NETMAKER_BASE_DOMAIN/g" /root/docker-compose.yml
 sed -i "s/SERVER_PUBLIC_IP/$SERVER_PUBLIC_IP/g" /root/docker-compose.yml
 sed -i "s/COREDNS_IP/$COREDNS_IP/g" /root/docker-compose.yml
-sed -i "s/REPLACE_MASTER_KEY/$REPLACE_MASTER_KEY/g" /root/docker-compose.yml
+sed -i "s/REPLACE_MASTER_KEY/$MASTER_KEY/g" /root/docker-compose.yml
 
 echo "starting containers..."
 
@@ -102,8 +123,55 @@ cat << "EOF"
 
 EOF
 
-echo "             finished installing"
-echo " "
-echo "             visit dashboard.$NETMAKER_BASE_DOMAIN to log in"
-echo " "
-echo " "
+
+echo "visit dashboard.$NETMAKER_BASE_DOMAIN to log in"
+echo""
+sleep 2
+
+if [ "${NETWORK_SETUP}" == "off" ]; then
+	echo "install complete"
+	exit 0
+fi
+
+echo "creating default network (10.101.0.0/16)"
+
+curl -d '{"addressrange":"10.101.0.0/16","netid":"default"}' -H "Authorization: Bearer $MASTER_KEY" -H 'Content-Type: application/json' localhost:8081/api/networks
+
+sleep 2
+
+echo "creating default key"
+
+curlresponse=$(curl -s -d '{"uses":99999,"name":"defaultkey"}' -H "Authorization: Bearer $MASTER_KEY" -H 'Content-Type: application/json' localhost:8081/api/networks/default/keys)
+ACCESS_TOKEN=$(jq -r '.accessstring' <<< ${curlresponse})
+
+sleep 2
+
+echo "configuring netmaker server as ingress gateway"
+
+curlresponse=$(curl -s -H "Authorization: Bearer $MASTER_KEY" -H 'Content-Type: application/json' localhost:8081/api/nodes/default)
+SERVER_ID=$(jq -r '.[0].macaddress' <<< ${curlresponse})
+
+curl -X POST -H "Authorization: Bearer $MASTER_KEY" -H 'Content-Type: application/json' localhost:8081/api/nodes/default/$SERVER_ID/createingress
+
+echo "finished configuring server and network. You can now add clients."
+echo ""
+echo ""
+echo "For Linux and Mac clients, install with the following command:"
+echo "        curl -sfL https://raw.githubusercontent.com/gravitl/netmaker/develop/scripts/netclient-install.sh | sudo KEY=$ACCESS_TOKEN sh -"
+echo ""
+echo ""
+echo "For Windows clients, perform the following from powershell, as administrator:"
+echo "        1. Make sure WireGuardNT is installed - https://download.wireguard.com/windows-client/wireguard-installer.exe"
+echo "        2. Download netclient.exe - wget https://github.com/gravitl/netmaker/releases/download/latest/netclient.exe"
+echo "        3. Install Netclient - powershell.exe .\\netclient.exe join -t $ACCESS_TOKEN"
+echo "        4. Whitelist C:\ProgramData\Netclient in Windows Defender"
+echo ""
+echo ""
+echo "For Android and iOS clients, perform the following steps:"
+echo "        1. Log into UI at dashboard.$NETMAKER_BASE_DOMAIN"
+echo "        2. Navigate to \"EXTERNAL CLIENTS\" tab"
+echo "        3. Select the gateway and create clients"
+echo "        4. Scan the QR Code from WireGuard app in iOS or Android"
+echo ""
+echo ""
+echo "Netmaker setup is now complete. You are ready to begin using Netmaker."

+ 87 - 1
servercfg/serverconf.go

@@ -7,10 +7,12 @@ import (
 	"net/http"
 	"os"
 	"strconv"
+	"strings"
 
 	"github.com/gravitl/netmaker/config"
 )
 
+// SetHost - sets the host ip
 func SetHost() error {
 	remoteip, err := GetPublicIP()
 	if err != nil {
@@ -19,6 +21,8 @@ func SetHost() error {
 	os.Setenv("SERVER_HOST", remoteip)
 	return nil
 }
+
+// GetServerConfig - gets the server config into memory from file or env
 func GetServerConfig() config.ServerConfig {
 	var cfg config.ServerConfig
 	cfg.APIConnString = GetAPIConnString()
@@ -65,8 +69,29 @@ func GetServerConfig() config.ServerConfig {
 	cfg.Database = GetDB()
 	cfg.Platform = GetPlatform()
 	cfg.Version = GetVersion()
+
+	// == auth config ==
+	var authInfo = GetAuthProviderInfo()
+	cfg.AuthProvider = authInfo[0]
+	cfg.ClientID = authInfo[1]
+	cfg.ClientSecret = authInfo[2]
+	cfg.FrontendURL = GetFrontendURL()
+
 	return cfg
 }
+
+// GetFrontendURL - gets the frontend url
+func GetFrontendURL() string {
+	var frontend = ""
+	if os.Getenv("FRONTEND_URL") != "" {
+		frontend = os.Getenv("FRONTEND_URL")
+	} else if config.Config.Server.FrontendURL != "" {
+		frontend = config.Config.Server.FrontendURL
+	}
+	return frontend
+}
+
+// GetAPIConnString - gets the api connections string
 func GetAPIConnString() string {
 	conn := ""
 	if os.Getenv("SERVER_API_CONN_STRING") != "" {
@@ -76,13 +101,17 @@ func GetAPIConnString() string {
 	}
 	return conn
 }
+
+// GetVersion - version of netmaker
 func GetVersion() string {
-	version := "0.8.4"
+	version := "0.8.5"
 	if config.Config.Server.Version != "" {
 		version = config.Config.Server.Version
 	}
 	return version
 }
+
+// GetDB - gets the database type
 func GetDB() string {
 	database := "sqlite"
 	if os.Getenv("DATABASE") != "" {
@@ -92,6 +121,8 @@ func GetDB() string {
 	}
 	return database
 }
+
+// GetAPIHost - gets the api host
 func GetAPIHost() string {
 	serverhost := "127.0.0.1"
 	remoteip, _ := GetPublicIP()
@@ -108,6 +139,8 @@ func GetAPIHost() string {
 	}
 	return serverhost
 }
+
+// GetPodIP - get the pod's ip
 func GetPodIP() string {
 	podip := "127.0.0.1"
 	if os.Getenv("POD_IP") != "" {
@@ -116,6 +149,7 @@ func GetPodIP() string {
 	return podip
 }
 
+// GetAPIPort - gets the api port
 func GetAPIPort() string {
 	apiport := "8081"
 	if os.Getenv("API_PORT") != "" {
@@ -126,6 +160,7 @@ func GetAPIPort() string {
 	return apiport
 }
 
+// GetCheckinInterval - get check in interval for nodes
 func GetCheckinInterval() string {
 	seconds := "15"
 	if os.Getenv("CHECKIN_INTERVAL") != "" {
@@ -136,6 +171,7 @@ func GetCheckinInterval() string {
 	return seconds
 }
 
+// GetDefaultNodeLimit - get node limit if one is set
 func GetDefaultNodeLimit() int32 {
 	var limit int32
 	limit = 999999999
@@ -147,6 +183,8 @@ func GetDefaultNodeLimit() int32 {
 	}
 	return limit
 }
+
+// GetGRPCConnString - get grpc conn string
 func GetGRPCConnString() string {
 	conn := ""
 	if os.Getenv("SERVER_GRPC_CONN_STRING") != "" {
@@ -157,6 +195,7 @@ func GetGRPCConnString() string {
 	return conn
 }
 
+// GetCoreDNSAddr - gets the core dns address
 func GetCoreDNSAddr() string {
 	addr, _ := GetPublicIP()
 	if os.Getenv("COREDNS_ADDR") != "" {
@@ -167,6 +206,7 @@ func GetCoreDNSAddr() string {
 	return addr
 }
 
+// GetGRPCHost - get the grpc host url
 func GetGRPCHost() string {
 	serverhost := "127.0.0.1"
 	remoteip, _ := GetPublicIP()
@@ -183,6 +223,8 @@ func GetGRPCHost() string {
 	}
 	return serverhost
 }
+
+// GetGRPCPort - gets the grpc port
 func GetGRPCPort() string {
 	grpcport := "50051"
 	if os.Getenv("GRPC_PORT") != "" {
@@ -192,6 +234,8 @@ func GetGRPCPort() string {
 	}
 	return grpcport
 }
+
+// GetMasterKey - gets the configured master key of server
 func GetMasterKey() string {
 	key := "secretkey"
 	if os.Getenv("MASTER_KEY") != "" {
@@ -201,6 +245,8 @@ func GetMasterKey() string {
 	}
 	return key
 }
+
+// GetAllowedOrigin - get the allowed origin
 func GetAllowedOrigin() string {
 	allowedorigin := "*"
 	if os.Getenv("CORS_ALLOWED_ORIGIN") != "" {
@@ -210,6 +256,8 @@ func GetAllowedOrigin() string {
 	}
 	return allowedorigin
 }
+
+// IsRestBackend - checks if rest is on or off
 func IsRestBackend() bool {
 	isrest := true
 	if os.Getenv("REST_BACKEND") != "" {
@@ -223,6 +271,8 @@ func IsRestBackend() bool {
 	}
 	return isrest
 }
+
+// IsAgentBackend - checks if agent backed is on or off
 func IsAgentBackend() bool {
 	isagent := true
 	if os.Getenv("AGENT_BACKEND") != "" {
@@ -236,6 +286,8 @@ func IsAgentBackend() bool {
 	}
 	return isagent
 }
+
+// IsClientMode - checks if it should run in client mode
 func IsClientMode() string {
 	isclient := "on"
 	if os.Getenv("CLIENT_MODE") != "" {
@@ -255,6 +307,8 @@ func IsClientMode() string {
 	}
 	return isclient
 }
+
+// IsDNSMode - should it run with DNS
 func IsDNSMode() bool {
 	isdns := true
 	if os.Getenv("DNS_MODE") != "" {
@@ -269,6 +323,7 @@ func IsDNSMode() bool {
 	return isdns
 }
 
+// IsGRPCSSL - ssl grpc on or off
 func IsGRPCSSL() bool {
 	isssl := false
 	if os.Getenv("GRPC_SSL") != "" {
@@ -283,6 +338,7 @@ func IsGRPCSSL() bool {
 	return isssl
 }
 
+// DisableRemoteIPCheck - disable the remote ip check
 func DisableRemoteIPCheck() bool {
 	disabled := false
 	if os.Getenv("DISABLE_REMOTE_IP_CHECK") != "" {
@@ -296,6 +352,8 @@ func DisableRemoteIPCheck() bool {
 	}
 	return disabled
 }
+
+// DisableDefaultNet - disable default net
 func DisableDefaultNet() bool {
 	disabled := false
 	if os.Getenv("DISABLE_DEFAULT_NET") != "" {
@@ -309,6 +367,8 @@ func DisableDefaultNet() bool {
 	}
 	return disabled
 }
+
+// GetPublicIP - gets public ip
 func GetPublicIP() (string, error) {
 
 	endpoint := ""
@@ -335,6 +395,8 @@ func GetPublicIP() (string, error) {
 	}
 	return endpoint, err
 }
+
+// GetVerbose - get the verbosity of server
 func GetVerbose() int32 {
 	level, err := strconv.Atoi(os.Getenv("VERBOSITY"))
 	if err != nil || level < 0 {
@@ -346,6 +408,7 @@ func GetVerbose() int32 {
 	return int32(level)
 }
 
+// GetPlatform - get the system type of server
 func GetPlatform() string {
 	platform := "linux"
 	if os.Getenv("PLATFORM") != "" {
@@ -356,6 +419,7 @@ func GetPlatform() string {
 	return platform
 }
 
+// GetSQLConn - get the sql connection string
 func GetSQLConn() string {
 	sqlconn := "http://"
 	if os.Getenv("SQL_CONN") != "" {
@@ -366,6 +430,7 @@ func GetSQLConn() string {
 	return sqlconn
 }
 
+// IsSplitDNS - checks if split dns is on
 func IsSplitDNS() bool {
 	issplit := false
 	if os.Getenv("IS_SPLIT_DNS") == "yes" {
@@ -376,6 +441,7 @@ func IsSplitDNS() bool {
 	return issplit
 }
 
+// GetNodeID - gets the node id
 func GetNodeID() string {
 	var id string
 	id = getMacAddr()
@@ -387,6 +453,7 @@ func GetNodeID() string {
 	return id
 }
 
+// GetServerCheckinInterval - gets the server check-in time
 func GetServerCheckinInterval() int64 {
 	var t = int64(5)
 	var envt, _ = strconv.Atoi(os.Getenv("SERVER_CHECKIN_INTERVAL"))
@@ -398,6 +465,25 @@ func GetServerCheckinInterval() int64 {
 	return t
 }
 
+// GetAuthProviderInfo = gets the oauth provider info
+func GetAuthProviderInfo() []string {
+	var authProvider = ""
+	if os.Getenv("AUTH_PROVIDER") != "" && os.Getenv("CLIENT_ID") != "" && os.Getenv("CLIENT_SECRET") != "" {
+		authProvider = strings.ToLower(os.Getenv("AUTH_PROVIDER"))
+		if authProvider == "google" || authProvider == "azure-ad" || authProvider == "github" {
+			return []string{authProvider, os.Getenv("CLIENT_ID"), os.Getenv("CLIENT_SECRET")}
+		} else {
+			authProvider = ""
+		}
+	} else if config.Config.Server.AuthProvider != "" && config.Config.Server.ClientID != "" && config.Config.Server.ClientSecret != "" {
+		authProvider = strings.ToLower(config.Config.Server.AuthProvider)
+		if authProvider == "google" || authProvider == "azure-ad" || authProvider == "github" {
+			return []string{authProvider, config.Config.Server.ClientID, config.Config.Server.ClientSecret}
+		}
+	}
+	return []string{"", "", ""}
+}
+
 // GetMacAddr - get's mac address
 func getMacAddr() string {
 	ifas, err := net.Interfaces()

+ 1 - 1
servercfg/sqlconf.go

@@ -1,8 +1,8 @@
 package servercfg
 
 import (
-	"os"
 	"github.com/gravitl/netmaker/config"
+	"os"
 	"strconv"
 )
 

+ 5 - 4
serverctl/serverctl.go

@@ -90,7 +90,7 @@ func InitServerNetclient() error {
 
 // HandleContainedClient - function for checkins on server
 func HandleContainedClient() error {
-	servernets, err := models.GetNetworks()
+	servernets, err := logic.GetNetworks()
 	if err != nil && !database.IsEmptyRecord(err) {
 		return err
 	}
@@ -107,9 +107,11 @@ func HandleContainedClient() error {
 			err = logic.ServerCheckin(servercfg.GetNodeID(), serverNet.NetID)
 			if err != nil {
 				logic.Log("error occurred during server checkin: "+err.Error(), 1)
+			} else {
+				logic.Log("completed peers check of network "+serverNet.NetID, 3)
 			}
 		}
-		logic.Log("completed a checkin call", 3)
+		// logic.Log("completed a checkin call", 3)
 	}
 	return nil
 }
@@ -168,7 +170,6 @@ func SyncNetworks(servernets []models.Network) error {
 
 // AddNetwork - add a network to server in client mode
 func AddNetwork(network string) (bool, error) {
-	err := logic.ServerJoin(network, servercfg.GetNodeID(), "")
-	logic.Log("server added to network "+network, 2)
+	var err = logic.ServerJoin(network, servercfg.GetNodeID(), "")
 	return true, err
 }

+ 1 - 1
models/validation.go → validation/validation.go

@@ -1,4 +1,4 @@
-package models
+package validation
 
 import (
 	"regexp"