mirror of
https://github.com/gravitl/netmaker.git
synced 2026-10-12 02:15:06 +00:00
* wip(go): return auth token with user on create org owner; * wip(go): prevent use of default tenant slug ; * wip(go): upsert settings for tenant in ctx; * wip(go): use passed ctx when updating user; remove password validation from ValidateUser (already validated in UpdateUser); * wip(go): qualify all tenant id usages with table name; * wip(go): use passed ctx * wip(go): return old enrollment key type on create; * wip(go): prevent null networks return; * wip(go): wire ctx in dns/nameserver functions; wire ctx in host registration functions; * wip(go): wire ctx in node functions; * wip(go): wire ctx in node functions; * wip(go): wire ctx in extclient functions; * wip(go): remove not needed field; * wip(go): use passed ctx; * wip(go): use unique index instead of primary key; * wip(go): wire ctx in extclient apis; * wip(go): wire ctx in extclient apis; * wip(go): remove org id check from global scoped get tenant api; * wip(go): add scope slug to discover response; * wip(go): wire ctx in get users; * wip(go): set tenant id in posture check violations; * wip(go): upsert membership instead of user; * wip(go): pass correct len; * wip(go): optimize (single loop); * wip(go): unify todos; * wip(go): add todo; * wip(go): wire context for network get; * wip(go): wire context; * wip(go): wire context; * wip(go): wire ctx when getting tenant superadmin; * wip(go): add network creator to network admin group; * wip(go): check for user membership on sync; * wip(go): add methods with strict membership check; * wip(go): remove unwanted feature flags; * wip(go): report per-tenant usage during license validation; * wip(go): move email setting from tenant to org; * wip(go): pass org scope to email init; * wip(go): remove old tenant email settings usages; use org settings to get email settings * wip(go): use get email settings from org settings; * wip(go): remove email settings migration; * wip(go): migrate email settings from server to org settings; * wip(go): assign global network admin group membership only for super-admin and admins; * wip(go): assign global network admin group membership only for super-admin and admins; * wip(go): add membership alias to filter; * wip(go): skip clearing local license validation response cache; * wip(go): extend license validation response for multi-tenancy; * wip(go): add functions to get sole tenant and org; * wip(go): use tenant-specific feature flags; * wip(go): add tenant status type; * wip(go): remove old hook comment; * wip(go): expect ctx during license validation; * wip(go): always create default org and tenant for ce; * wip(go): add tenant-specific check for license validation error; * wip(go): cache license validation response in memory; * wip(go): add todo to handle multiple-tenant for integration pull; * wip(go): pass ctx during license validation; * wip(go): add todo for making changes later; run hook separately for each tenant; * wip(go): return specific errors when license validation response not in cache; * wip(go): use retryable.httpclient for calling license validation api; * wip(go): process license validation; * wip(go): license validation function; * wip(go): migration on license validation; * wip(go): org settings not expected; * wip(go): remove expiry from license validation response; * wip(go): add limits and limits enforcement to license validation response; * wip(go): remove expiry usages; * wip(go): add arg to clear cache; * wip(go): add arg to clear cache; * wip(go): skip license validation if cached resp; * wip(go): allow org_id and tenant_id query params; * wip(go): upsert tenant membership on sync; * wip(go): pass ctx to delete network roles; * wip(go): create tenant membership on org owner creation on ce; * wip(go): add tenant orchestrator; * wip(go): use tenant orchestrator; * wip(go): add default settings for tenant; * wip(go): make org owner superadmin on all tenants; * wip(go): simplify SyncOrgAndTenants; * wip(go): retry license validation only 3 times * wip(go): store invalid err; * wip(go): add function to check if limits are to be enforced on a tenant; * wip(go): pass tenant scope if possible when checking for license validation error; * wip(go): remove merge errors; * wip(go): add functions for limit checks; * wip(go): wire host limit check; * wip(go): wire ingress limit check; * wip(go): wire egress limit check; * wip(go): wire client limit check; * wip(go): wire network limit check; * wip(go): wire user limit check; * wip(go): use correct errors; * wip(go): skip telemetry for msps; * wip(go): disable telemetry on msp tenants; * wip(go): err check; * wip(go): uncomment log event; * wip(go): get sso state to get scope for non-headless oauth login; * wip(go): inline delete pending user; * wip(go): allow both org and tenant scope for pending users table; * wip(go): add migration for pending user table during set tenant id and rekey org/tenant id; * wip(go): update queries to use new scope fields in pending users table; * wip(go): allow both org and tenant scope for user invites table; * wip(go): update queries to use new scope fields; * wip(go): add migration for rekey-ing org/tenant id and setting tenant id; * wip(go): inline delete user invite; * wip(go): set scope when creating invite; * wip(go): get user count with membership is ctx is tenant scoped; * wip(go): add methods to upsert and delete org memberships; * wip(go): add global valid tenant and org roles map; * wip(go): add scope query in user invite get by email; * wip(go): add support for upsert and delete of org and tenant membership both; * wip(go): infer scope for user invite apis; * wip(go): wire ctx in get user invite by email; * wip(go): allow no scope headers to be passed in infer scope apis; * wip(go): add validation and checks for org-scoped invite api handlers; * wip(go): wire ctx to is pending user check; * wip(go): infer scope for pending user apis; * wip(go): set default role based on ctx scope; * wip(go): move mfa to tenant membership table; move account status to membership tables; * wip(go): migrate account status and mfa settings; * wip(go): update account status on sync; * wip(go): use update mfa to update mfa setting; * wip(go): set new membership settings on user creation; * wip(go): use membership where required; * wip(go): wire ctx on key unlink refs; * wip(go): use r.Context directly without reinitializing db; * wip(go): wire ctx in acls; * wip(go): add tenant scoped primary key constructor; * wip(go): use tenant scoped primary key for acls, dns, extclients and tags; * wip(go): wire ctx for get groups in network; * wip(go): wire ctx for get groups; * wip(go): scope non-uuid group ids by tenant id; * wip(go): migrate non-uuid group ids to scoped ids; * wip(go): don't expect scope id in invite validation and signup apis; * wip(go): wire ctx for get matching user; allow org scope when querying by external idp id; * wip(go): add mfa to org; * wip(go): scope preauth token; * wip(go): add fallback for older auth tokens; * wip(go): wire ctx for preauth token creation; * wip(go): migrate mfa settings; * wip(go): resolve mfa settings for inherited auth; * wip(go): update mfa for org; * wip(go): infer scope for totp apis; * wip(go): use ctx when passed; * wip(go): infer scope for list user apis; * wip(go): ensure set primary keys don't cause errors during fetch; * wip(go): make primary key for integrations uuid and store provider in separate key; * wip(go): migrate integration ids; * wip(go): use provider instead of id everywhere; * wip(go): remove gorm column; * wip(go): wire ctx for igw egress creation; * wip(go): wire ctx for igw egress update; * wip(go): wire ctx for host registration; * wip(go): wire ctx for host registration; * wip(go): merge errors; * wip(go): merge errors; * wip(go): wire ctx; remove default scope usages; * wip(go): use get with membership; * wip(go): org scope license validation; * wip(go): remove debug log; * wip(go): remove scoping on list roles; * wip(go): ignore invite creation if already exists; * wip(go): add is msp func to check if deployment is msp deployment; * wip(go): ensure username is email for msp users; * wip(go): add email validated check on user table; * wip(go): mark email validated when oauth user; * wip(go): handle gorm error; * wip(go): add tenant id to siem grpc schema; * wip(go): read tenant from scoped ctx; * wip(go): pass scoped ctx to siem grpc client; * wip(go): remove unused publisher; * wip(go): publish all tenant exporter updates on pull; * wip(go): fix wg usage; * wip(go): tenant scope the ctx when getting server info; * wip(go): add tenant id to enrollment key token; * wip(go): make host register tenant scope; * wip(go): add default settings; * wip(go): use correct checks for is msp tenant; * wip(go): add email validation extension; * wip(go): add pages for validation check; * wip(go): use extension for validating user; * wip(go): add invite type; * wip(go): create only one validation invite per user email; * wip(go): add validate email handler; * wip(go): add email builder; * wip(go): add user invite type migration logic; * wip(go): only log email send failures; * wip(go): add tenant id to flows table; * wip(go): add func to check if connected to clickhouse; * wip(go): migrate clickhouse; * wip(go): add tenant id to mdm and edr state; * wip(go): add delete methods for tables; * wip(go): add tenant teardown method; * wip(go): handle tenant deletion; * wip(go): clean up flow logs on tenant deletion; * wip(go): add update org membership role id method; * wip(go): add transfer org ownership api; * wip(go): move audit logs retention period to org settings; * wip(go):get retention period from org settings; * wip(go): remove old audit logs usage; * wip(go): migrate audit logs retention period; * wip(go): unmarshal decodes nums as float64; * wip(go): run flow logs cleanup for all tenants in one go; * wip(go): add audit logs retention period and smtp settings to tenant settings table for non-msp tenants; * wip(go): resolve retention period and smtp settings correctly; * wip(go): handle retention period and smtp settings updates and fetches correctly; * wip(go): overlay retention period and smtp settings on server settings;; * wip(go): upsert overlayed settings; * wip(go): add azure tenant id to org settings; * wip(go): tenant scope list roles; * wip(go): add noop creds; * wip(go): use first instead of find; * wip(go): use correct roles when transfering ownership; * wip(go): delete access tokens only if tenant scoped; * wip(go): make delete user work for both org and tenant scope; * wip(go): delete extclients only if tenant scoped; * wip(go): check if caller is allowed to delete user; * wip(go): infer scope for update user; * wip(go): prevent org-owner elevation; * wip(go): permissions check for update; * wip(go): delete user access tokens only if tenant scope; * wip(go): delete extclients only if tenant scope; * wip(go): move upsert membership in upsert user; * wip(go): set api host when constructing server config; * wip(go): fix static errors; * wip(go): fix test cases; * wip(go): use correct get ns order; * wip(go): use correct get pending host order; * wip(go): use correct get access token order; * wip(go): use correct get event order; * wip(go): return error response on emqx user creation error; * wip(go): store correct value in cache; * wip(go): return better error response; * wip(go): return better error response; * wip(go): use cache when listing egress acls; * wip(go): wire ctx in UserHasNetworkGroupAccess; * wip(go): remove double scope wrapping; * wip(go): add security check for list and get org; * wip(go): ratelimit discover api; * wip(go): expect sole org id in get and create org owner; * wip(go): wrap create org owner in tx;
303 lines
7.1 KiB
Go
303 lines
7.1 KiB
Go
// package for logicing client and server code
|
|
package logic
|
|
|
|
import (
|
|
"context"
|
|
"crypto/rand"
|
|
"encoding/base32"
|
|
"encoding/base64"
|
|
"fmt"
|
|
"log/slog"
|
|
"net"
|
|
"net/http"
|
|
"os"
|
|
"reflect"
|
|
"regexp"
|
|
"strings"
|
|
"time"
|
|
"unicode"
|
|
|
|
"github.com/blang/semver"
|
|
"github.com/c-robinson/iplib"
|
|
"github.com/gravitl/netmaker/logger"
|
|
"github.com/gravitl/netmaker/schema"
|
|
)
|
|
|
|
// IsBase64 - checks if a string is in base64 format
|
|
// This is used to validate public keys (make sure they're base64 encoded like all public keys should be).
|
|
func IsBase64(s string) bool {
|
|
_, err := base64.StdEncoding.DecodeString(s)
|
|
return err == nil
|
|
}
|
|
|
|
// CheckEndpoint - checks if an endpoint is valid
|
|
func CheckEndpoint(endpoint string) bool {
|
|
endpointarr := strings.Split(endpoint, ":")
|
|
return len(endpointarr) == 2
|
|
}
|
|
|
|
// FileExists - checks if local file exists
|
|
func FileExists(f string) bool {
|
|
info, err := os.Stat(f)
|
|
if os.IsNotExist(err) {
|
|
return false
|
|
}
|
|
return !info.IsDir()
|
|
}
|
|
|
|
// IsAddressInCIDR - util to see if an address is in a cidr or not
|
|
func IsAddressInCIDR(address net.IP, cidr string) bool {
|
|
var _, currentCIDR, cidrErr = net.ParseCIDR(cidr)
|
|
if cidrErr != nil {
|
|
return false
|
|
}
|
|
return currentCIDR.Contains(address)
|
|
}
|
|
|
|
// SetNetworkNodesLastModified - sets the network nodes last modified
|
|
func SetNetworkNodesLastModified(ctx context.Context, networkName string) error {
|
|
_network := &schema.Network{
|
|
Name: networkName,
|
|
NodesUpdatedAt: time.Now(),
|
|
}
|
|
return _network.UpdateNodesUpdatedAt(ctx)
|
|
}
|
|
|
|
// RandomString - returns a random string in a charset
|
|
func RandomString(length int) string {
|
|
randombytes := make([]byte, length)
|
|
_, err := rand.Read(randombytes)
|
|
if err != nil {
|
|
logger.Log(0, "random string", err.Error())
|
|
return ""
|
|
}
|
|
return base32.StdEncoding.EncodeToString(randombytes)[:length]
|
|
}
|
|
|
|
// StringSliceContains - sees if a string slice contains a string element
|
|
func StringSliceContains(slice []string, item string) bool {
|
|
for _, s := range slice {
|
|
if s == item {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
func SetVerbosity(logLevel int) {
|
|
var level slog.Level
|
|
switch logLevel {
|
|
|
|
case 0:
|
|
level = slog.LevelInfo
|
|
case 1:
|
|
level = slog.LevelError
|
|
case 2:
|
|
level = slog.LevelWarn
|
|
case 3:
|
|
level = slog.LevelDebug
|
|
|
|
default:
|
|
level = slog.LevelInfo
|
|
}
|
|
// Create the logger with the chosen level
|
|
handler := slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
|
|
Level: level,
|
|
})
|
|
logger := slog.New(handler)
|
|
slog.SetDefault(logger)
|
|
|
|
}
|
|
|
|
// NormalizeCIDR - returns the first address of CIDR
|
|
func NormalizeCIDR(address string) (string, error) {
|
|
ip, IPNet, err := net.ParseCIDR(address)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if ip.To4() == nil {
|
|
net6 := iplib.Net6FromStr(IPNet.String())
|
|
IPNet.IP = net6.FirstAddress()
|
|
} else {
|
|
net4 := iplib.Net4FromStr(IPNet.String())
|
|
IPNet.IP = net4.NetworkAddress()
|
|
}
|
|
return IPNet.String(), nil
|
|
}
|
|
|
|
// StringDifference - returns the elements in `a` that aren't in `b`.
|
|
func StringDifference(a, b []string) []string {
|
|
mb := make(map[string]struct{}, len(b))
|
|
for _, x := range b {
|
|
mb[x] = struct{}{}
|
|
}
|
|
var diff []string
|
|
for _, x := range a {
|
|
if _, found := mb[x]; !found {
|
|
diff = append(diff, x)
|
|
}
|
|
}
|
|
return diff
|
|
}
|
|
|
|
// CheckIfFileExists - checks if file exists or not in the given path
|
|
func CheckIfFileExists(filePath string) bool {
|
|
if _, err := os.Stat(filePath); os.IsNotExist(err) {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
// RemoveStringSlice - removes an element at given index i
|
|
// from a given string slice
|
|
func RemoveStringSlice(slice []string, i int) []string {
|
|
return append(slice[:i], slice[i+1:]...)
|
|
}
|
|
|
|
// RemoveAllFromSlice removes every occurrence of val from s (stable order).
|
|
func RemoveAllFromSlice[T comparable](s []T, val T) []T {
|
|
// Reuse the underlying array: write filtered items back into s[:0].
|
|
out := s[:0]
|
|
for _, v := range s {
|
|
if v != val {
|
|
out = append(out, v)
|
|
}
|
|
}
|
|
// out now contains only the kept items; capacity unchanged, len shrunk.
|
|
return out
|
|
}
|
|
|
|
// IsSlicesEqual tells whether a and b contain the same elements.
|
|
// A nil argument is equivalent to an empty slice.
|
|
func IsSlicesEqual(a, b []string) bool {
|
|
if len(a) != len(b) {
|
|
return false
|
|
}
|
|
for i, v := range a {
|
|
if v != b[i] {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// VersionLessThan checks if v1 < v2 semantically
|
|
// dev is the latest version
|
|
func VersionLessThan(v1, v2 string) (bool, error) {
|
|
if v1 == "dev" {
|
|
return false, nil
|
|
}
|
|
if v2 == "dev" {
|
|
return true, nil
|
|
}
|
|
semVer1 := strings.TrimFunc(v1, func(r rune) bool {
|
|
return !unicode.IsNumber(r)
|
|
})
|
|
semVer2 := strings.TrimFunc(v2, func(r rune) bool {
|
|
return !unicode.IsNumber(r)
|
|
})
|
|
sv1, err := semver.Parse(semVer1)
|
|
if err != nil {
|
|
return false, fmt.Errorf("failed to parse semver1 (%s): %w", semVer1, err)
|
|
}
|
|
sv2, err := semver.Parse(semVer2)
|
|
if err != nil {
|
|
return false, fmt.Errorf("failed to parse semver2 (%s): %w", semVer2, err)
|
|
}
|
|
return sv1.LT(sv2), nil
|
|
}
|
|
|
|
// Compare any two maps with any key and value types
|
|
func CompareMaps[K comparable, V any](a, b map[K]V) bool {
|
|
if len(a) != len(b) {
|
|
return false
|
|
}
|
|
|
|
for key, valA := range a {
|
|
valB, ok := b[key]
|
|
if !ok {
|
|
return false
|
|
}
|
|
|
|
if !reflect.DeepEqual(valA, valB) {
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func UniqueStrings(input []string) []string {
|
|
seen := make(map[string]struct{})
|
|
var result []string
|
|
|
|
for _, val := range input {
|
|
if _, ok := seen[val]; !ok {
|
|
seen[val] = struct{}{}
|
|
result = append(result, val)
|
|
}
|
|
}
|
|
|
|
return result
|
|
}
|
|
func GetClientIP(r *http.Request) string {
|
|
// Trust X-Forwarded-For first
|
|
if xff := r.Header.Get("X-Forwarded-For"); xff != "" {
|
|
parts := strings.Split(xff, ",")
|
|
return strings.TrimSpace(parts[0])
|
|
}
|
|
if xrip := r.Header.Get("X-Real-IP"); xrip != "" {
|
|
return xrip
|
|
}
|
|
|
|
ip, _, err := net.SplitHostPort(r.RemoteAddr)
|
|
if err != nil {
|
|
return r.RemoteAddr
|
|
}
|
|
return ip
|
|
}
|
|
|
|
// CompareIfaceSlices compares two slices of Iface for deep equality (order-sensitive)
|
|
func CompareIfaceSlices(a, b []schema.Iface) bool {
|
|
if len(a) != len(b) {
|
|
return false
|
|
}
|
|
for i := range a {
|
|
if !compareIface(a[i], b[i]) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
func compareIface(a, b schema.Iface) bool {
|
|
return a.Name == b.Name &&
|
|
a.Address.IP.Equal(b.Address.IP) &&
|
|
a.Address.Mask.String() == b.Address.Mask.String() &&
|
|
a.AddressString == b.AddressString
|
|
}
|
|
|
|
// IsEgressDomainPattern returns true for a normal FQDN or a single-label wildcard prefix form (*.example.com).
|
|
func IsEgressDomainPattern(domain string) bool {
|
|
domain = strings.TrimSpace(domain)
|
|
if domain == "" {
|
|
return false
|
|
}
|
|
if strings.HasPrefix(domain, "*.") {
|
|
return IsFQDN(strings.TrimPrefix(domain, "*."))
|
|
}
|
|
return IsFQDN(domain)
|
|
}
|
|
|
|
// IsFQDN checks if the given string is a valid Fully Qualified Domain Name (FQDN)
|
|
func IsFQDN(domain string) bool {
|
|
// Basic check to ensure the domain is not empty and has at least one dot (.)
|
|
if domain == "" || !strings.Contains(domain, ".") {
|
|
return false
|
|
}
|
|
|
|
// Regular expression for validating FQDN (basic check for valid characters and structure)
|
|
fqdnRegex := `^(?i)([a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$`
|
|
re := regexp.MustCompile(fqdnRegex)
|
|
|
|
return re.MatchString(domain)
|
|
}
|