func CanUserAccessAllObjects(request *http.Request, orgID, objectType string) (bool, int, string) {
	code, userOrgID, userID := Authenticate(request)
	if trace.IsLogging(logger.DEBUG) {
		trace.Debug("In security.CanUserAccessAllObjects: authcode is %d, userOrgID is %s, userID is %s", code, userOrgID, userID)
	}
	if code == AuthSyncAdmin {
		return true, code, userID
	}
	if code == AuthFailed || code == AuthEdgeNode {
		return false, AuthFailed, ""
	}
	if common.Configuration.NodeType == common.ESS {
		if userOrgID != orgID {
			return false, AuthFailed, ""
		} else {
			return true, code, userID
		}
	}
	if code == AuthAdmin {
		if userOrgID != orgID {
			code = AuthUser
		} else {
			return true, code, userID
		}
	}
	if code == AuthUser || code == AuthNodeUser {
		if userOrgID != orgID {
			return false, code, userID
		}
		aclUserType := GetACLUserType(code)
		if checkObjectAccessByUser(userID, orgID, objectType, aclUserType) {
			if trace.IsLogging(logger.DEBUG) {
				trace.Debug("In security.CanUserAccessObject: checkObjectAccessByUser returns true for authcode %d for user %s", code, userID)
			}
			return true, code, userID
		}
		return false, code, userID
	}
	return false, AuthFailed, ""
}
CanUserAccessAllObjects checks if the user identified by the credentials in the supplied request,
can read the specified object type.
return values:
1) true indicates the given user can access all objects of given objectType in given orgID;
false indicates can access "public" objects of given objectType in given orgID only
2) authCode 3) userID
package security
import (
	"crypto/hmac"
	"crypto/rand"
	"crypto/sha256"
	"errors"
	"fmt"
	"net/http"
	"strings"
	"sync"
	"time"
	"github.com/open-horizon/edge-utilities/logger"
	"github.com/open-horizon/edge-utilities/logger/log"
	"github.com/open-horizon/edge-utilities/logger/trace"
	"github.com/open-horizon/edge-sync-service/common"
	"github.com/open-horizon/edge-sync-service/core/storage"
)
var authenticator Authentication
const saltLength = 24
type authenticationCacheElement struct {
	appSecret  []byte
	salt       []byte
	code       int
	orgID      string
	userid     string
	expiration time.Time
}
type destinationACLCacheElement struct {
	users      []common.ACLentry
	expiration time.Time
}
const SPIRequestIdentityHeader = "X-Sync-Service-Dest"
var spiRequestIdentity string
const cacheDuration = 15 * time.Minute
var authenticationCache map[string]authenticationCacheElement
var authenticationCacheLock sync.RWMutex
var destinationACLCache map[string]destinationACLCacheElement
var destinationACLCacheLock sync.RWMutex
var cacheFlushTicker *time.Ticker
var cacheFlushStopChannel chan int
var Store storage.Storage
func Start() {
	authenticator.Start()
	if common.Configuration.NodeType == common.ESS {
		spiRequestIdentity = common.Configuration.OrgID + "/" +
			common.Configuration.DestinationType + "/" + common.Configuration.DestinationID
	}
	authenticationCache = make(map[string]authenticationCacheElement)
	destinationACLCache = make(map[string]destinationACLCacheElement)
	cacheFlushStopChannel = make(chan int, 1)
	cacheFlushTicker = time.NewTicker(2 * cacheDuration)
	go func() {
		common.GoRoutineStarted()
		keepRunning := true
		for keepRunning {
			select {
			case <-cacheFlushTicker.C:
				flushAuthenticationCache()
				flushDestinationACLCache()
			case <-cacheFlushStopChannel:
				keepRunning = false
			}
		}
		cacheFlushTicker = nil
		common.GoRoutineEnded()
	}()
}
func Stop() {
	if cacheFlushTicker != nil {
		cacheFlushTicker.Stop()
		cacheFlushStopChannel <- 1
	}
}
func SetAuthentication(auth Authentication) {
	authenticator = auth
}
func Authenticate(request *http.Request) (int, string, string) {
	appKey, appSecret, ok := request.BasicAuth()
	if !ok {
		return AuthFailed, "", ""
	}
	authenticationCacheLock.RLock()
	entry, ok := authenticationCache[appKey]
	authenticationCacheLock.RUnlock()
	now := time.Now()
	if ok && now.Before(entry.expiration) {
		secretMAC, _, err := saltSecret(appSecret, entry.salt)
		if err != nil {
			if log.IsLogging(logger.ERROR) {
				log.Error("%s", err)
			}
			return AuthFailed, "", ""
		}
		if hmac.Equal(secretMAC, entry.appSecret) {
			return entry.code, entry.orgID, entry.userid
		}
	}
	code, orgID, userID := authenticator.Authenticate(request)
	if code != AuthFailed {
		secretMAC, salt, err := saltSecret(appSecret, nil)
		if err != nil {
			if log.IsLogging(logger.ERROR) {
				log.Error("%s", err)
			}
			return AuthFailed, "", ""
		}
		entry = authenticationCacheElement{secretMAC, salt, code, orgID, userID, now.Add(cacheDuration)}
		authenticationCacheLock.Lock()
		authenticationCache[appKey] = entry
		authenticationCacheLock.Unlock()
	} else {
		authenticationCacheLock.Lock()
		delete(authenticationCache, appKey)
		authenticationCacheLock.Unlock()
	}
	return code, orgID, userID
}
func CanUserCreateObject(request *http.Request, orgID string, metaData *common.MetaData) (bool, string, string) {
	code, userOrgID, userID := Authenticate(request)
	if trace.IsLogging(logger.DEBUG) {
		trace.Debug("In security.CanUserCreateObject: authcode is %d, userOrgID is %s, userID is %s", code, userOrgID, userID)
	}
	if code == AuthSyncAdmin {
		return true, userOrgID, userID
	}
	if code == AuthFailed || code == AuthEdgeNode || userOrgID != orgID {
		return false, userOrgID, userID
	}
	if common.Configuration.NodeType == common.ESS {
		return true, userOrgID, userID
	}
	if code == AuthAdmin {
		return true, userOrgID, userID
	}
	aclUserType := GetACLUserType(code)
	if !CheckObjectCanBeModifiedByUser(userID, orgID, metaData.ObjectType, aclUserType) {
		if log.IsLogging(logger.ERROR) {
			log.Error("checkObjectCanBeModifiedByUser is false")
		}
		return false, userOrgID, userID
	}
	if metaData.DestType == "" && len(metaData.DestinationsList) == 0 && metaData.DestinationPolicy == nil {
		if !checkUserHaveAccessToALLDestinations(userID, orgID, aclUserType) {
			if log.IsLogging(logger.ERROR) {
				log.Error("Must set \"DestinationType\" or \"DestinationList\" or \"DestinationPolicy\". Given userID %s (orgID: %s, aclUserType: %s) cannot broadcast object because user doesn't have access to all destination types", userID, orgID, aclUserType)
			}
			return false, userOrgID, userID
		}
	}
	destinationTypes := getDestinationTypes(metaData)
	if trace.IsLogging(logger.INFO) {
		trace.Info("Starting to check if the given user has access to the destination(s) defined in metadata")
	}
	for _, destinationType := range destinationTypes {
		if !checkDestinationAccessByUser(userID, orgID, destinationType, aclUserType) {
			if log.IsLogging(logger.ERROR) {
				log.Error("Given userID %s (orgID: %s, aclUserType: %s) doesn't have access to destination type: %s", userID, orgID, aclUserType, destinationType)
			}
			return false, userOrgID, userID
		}
	}
	return true, userOrgID, userID
}