Work on pm restructure
This commit is contained in:
@@ -6,6 +6,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Safing/portbase/database"
|
"github.com/Safing/portbase/database"
|
||||||
|
"github.com/Safing/portmaster/profile"
|
||||||
"github.com/tevino/abool"
|
"github.com/tevino/abool"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -76,6 +77,8 @@ func (p *Process) Delete() {
|
|||||||
if dbControllerFlag.IsSet() {
|
if dbControllerFlag.IsSet() {
|
||||||
dbController.PushUpdate(p)
|
dbController.PushUpdate(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
profile.DeactivateProfileSet(p.profileSet)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CleanProcessStorage cleans the storage from old processes.
|
// CleanProcessStorage cleans the storage from old processes.
|
||||||
|
|||||||
@@ -1,22 +1,81 @@
|
|||||||
package process
|
package process
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/Safing/portbase/database"
|
||||||
"github.com/Safing/portbase/log"
|
"github.com/Safing/portbase/log"
|
||||||
"github.com/Safing/portmaster/profile"
|
"github.com/Safing/portmaster/profile"
|
||||||
|
"github.com/Safing/portmaster/profile/index"
|
||||||
)
|
)
|
||||||
|
|
||||||
// FindProfiles finds and assigns a profile set to the process.
|
// FindProfiles finds and assigns a profile set to the process.
|
||||||
func (p *Process) FindProfiles() {
|
func (p *Process) FindProfiles() error {
|
||||||
|
|
||||||
// Get fingerprints of process
|
// Get fingerprints of process
|
||||||
|
|
||||||
// Check if user profile already exists, else create new
|
// Check if user profile already exists, else create new
|
||||||
|
pathIdentifier := profile.GetPathIdentifier(p.Path)
|
||||||
|
indexRecord, err := index.Get(pathIdentifier)
|
||||||
|
if err != nil && err != database.ErrNotFound {
|
||||||
|
log.Errorf("process: could not get profile index for %s: %s", pathIdentifier, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var possibleProfiles []*profile.Profile
|
||||||
|
if indexRecord != nil {
|
||||||
|
for _, profileID := range indexRecord.UserProfiles {
|
||||||
|
prof, err := profile.Get(profileID)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("process: failed to load profile %s: %s", profileID, err)
|
||||||
|
}
|
||||||
|
possibleProfiles = append(possibleProfiles, prof)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
prof := selectProfile(p, possibleProfiles)
|
||||||
|
if prof == nil {
|
||||||
|
// create new profile
|
||||||
|
prof := profile.New()
|
||||||
|
prof.Name = p.ExecName
|
||||||
|
prof.AddFingerprint(&profile.Fingerprint{
|
||||||
|
Type: "full_path",
|
||||||
|
Value: p.Path,
|
||||||
|
})
|
||||||
|
// TODO: maybe add sha256_sum?
|
||||||
|
prof.MarkUsed()
|
||||||
|
prof.Save()
|
||||||
|
}
|
||||||
|
|
||||||
// Find/Re-evaluate Stamp profile
|
// Find/Re-evaluate Stamp profile
|
||||||
|
// 1. check linked stamp profile
|
||||||
|
// 2. if last check is was more than a week ago, fetch from stamp:
|
||||||
|
// 3. send path identifier to stamp
|
||||||
|
// 4. evaluate all returned profiles
|
||||||
|
// 5. select best
|
||||||
|
// 6. link stamp profile to user profile
|
||||||
|
// FIXME: implement!
|
||||||
|
|
||||||
// p.UserProfileKey
|
if prof.MarkUsed() {
|
||||||
// p.profileSet
|
prof.Save()
|
||||||
|
}
|
||||||
|
|
||||||
|
p.UserProfileKey = prof.Key()
|
||||||
|
p.profileSet = profile.NewSet(prof, nil)
|
||||||
|
p.Save()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func selectProfile(p *Process, profs []*profile.Profile) (selectedProfile *profile.Profile) {
|
||||||
|
var highestScore int
|
||||||
|
for _, prof := range profs {
|
||||||
|
score := matchProfile(p, prof)
|
||||||
|
if score > highestScore {
|
||||||
|
selectedProfile = prof
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func matchProfile(p *Process, prof *profile.Profile) (score int) {
|
func matchProfile(p *Process, prof *profile.Profile) (score int) {
|
||||||
@@ -37,8 +96,10 @@ func matchFingerprint(p *Process, fp *profile.Fingerprint) (score int) {
|
|||||||
}
|
}
|
||||||
return profile.GetFingerprintWeight(fp.Type)
|
return profile.GetFingerprintWeight(fp.Type)
|
||||||
case "partial_path":
|
case "partial_path":
|
||||||
|
// FIXME: if full_path matches, do not match partial paths
|
||||||
return profile.GetFingerprintWeight(fp.Type)
|
return profile.GetFingerprintWeight(fp.Type)
|
||||||
case "md5_sum", "sha1_sum", "sha256_sum":
|
case "md5_sum", "sha1_sum", "sha256_sum":
|
||||||
|
// FIXME: one sum is enough, check sums in a grouped form, start with the best
|
||||||
sum, err := p.GetExecHash(fp.Type)
|
sum, err := p.GetExecHash(fp.Type)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("process: failed to get hash of executable: %s", err)
|
log.Errorf("process: failed to get hash of executable: %s", err)
|
||||||
|
|||||||
66
profile/active.go
Normal file
66
profile/active.go
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
package profile
|
||||||
|
|
||||||
|
import "sync"
|
||||||
|
|
||||||
|
var (
|
||||||
|
activeProfileSets = make(map[string]*Set)
|
||||||
|
activeProfileSetsLock sync.RWMutex
|
||||||
|
)
|
||||||
|
|
||||||
|
func activateProfileSet(set *Set) {
|
||||||
|
set.Lock()
|
||||||
|
defer set.Unlock()
|
||||||
|
activeProfileSetsLock.Lock()
|
||||||
|
defer activeProfileSetsLock.Unlock()
|
||||||
|
activeProfileSets[set.profiles[0].ID] = set
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeactivateProfileSet marks a profile set as not active.
|
||||||
|
func DeactivateProfileSet(set *Set) {
|
||||||
|
set.Lock()
|
||||||
|
defer set.Unlock()
|
||||||
|
activeProfileSetsLock.Lock()
|
||||||
|
defer activeProfileSetsLock.Unlock()
|
||||||
|
delete(activeProfileSets, set.profiles[0].ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateActiveUserProfile(profile *Profile) {
|
||||||
|
activeProfileSetsLock.RLock()
|
||||||
|
defer activeProfileSetsLock.RUnlock()
|
||||||
|
activeSet, ok := activeProfileSets[profile.ID]
|
||||||
|
if ok {
|
||||||
|
activeSet.Lock()
|
||||||
|
defer activeSet.Unlock()
|
||||||
|
activeSet.profiles[0] = profile
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateActiveGlobalProfile(profile *Profile) {
|
||||||
|
updateActiveProfile(1, profile)
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateActiveStampProfile(profile *Profile) {
|
||||||
|
updateActiveProfile(2, profile)
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateActiveFallbackProfile(profile *Profile) {
|
||||||
|
updateActiveProfile(3, profile)
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateActiveProfile(setID int, profile *Profile) {
|
||||||
|
activeProfileSetsLock.RLock()
|
||||||
|
defer activeProfileSetsLock.RUnlock()
|
||||||
|
|
||||||
|
for _, activeSet := range activeProfileSets {
|
||||||
|
activeSet.Lock()
|
||||||
|
activeProfile := activeSet.profiles[setID]
|
||||||
|
if activeProfile != nil {
|
||||||
|
activeProfile.Lock()
|
||||||
|
if activeProfile.ID == profile.ID {
|
||||||
|
activeSet.profiles[setID] = profile
|
||||||
|
}
|
||||||
|
activeProfile.Unlock()
|
||||||
|
}
|
||||||
|
activeSet.Unlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,8 +2,7 @@ package profile
|
|||||||
|
|
||||||
// Platform identifiers
|
// Platform identifiers
|
||||||
const (
|
const (
|
||||||
PlatformAny = "any"
|
PlatformLinux = "linux"
|
||||||
PlatformLinux = "lin"
|
PlatformWindows = "windows"
|
||||||
PlatformWindows = "win"
|
PlatformMac = "macos"
|
||||||
PlatformMac = "mac"
|
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -2,5 +2,5 @@ package profile
|
|||||||
|
|
||||||
// OS Identifier
|
// OS Identifier
|
||||||
const (
|
const (
|
||||||
osIdentifier = "mac"
|
osIdentifier = PlatformMac
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -2,5 +2,5 @@ package profile
|
|||||||
|
|
||||||
// OS Identifier
|
// OS Identifier
|
||||||
const (
|
const (
|
||||||
osIdentifier = "lin"
|
osIdentifier = PlatformLinux
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -12,8 +12,9 @@ import (
|
|||||||
|
|
||||||
// Namespaces
|
// Namespaces
|
||||||
const (
|
const (
|
||||||
UserNamespace = "user"
|
userNamespace = "user"
|
||||||
StampNamespace = "stamp"
|
stampNamespace = "stamp"
|
||||||
|
specialNamespace = "special"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|||||||
82
profile/defaults.go
Normal file
82
profile/defaults.go
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
package profile
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/Safing/portmaster/status"
|
||||||
|
)
|
||||||
|
|
||||||
|
func makeDefaultGlobalProfile() *Profile {
|
||||||
|
return &Profile{
|
||||||
|
ID: "global",
|
||||||
|
Name: "Global Profile",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeDefaultFallbackProfile() *Profile {
|
||||||
|
return &Profile{
|
||||||
|
ID: "fallback",
|
||||||
|
Name: "Fallback Profile",
|
||||||
|
Flags: map[uint8]uint8{
|
||||||
|
// Profile Modes
|
||||||
|
Blacklist: status.SecurityLevelDynamic,
|
||||||
|
Prompt: status.SecurityLevelSecure,
|
||||||
|
Whitelist: status.SecurityLevelFortress,
|
||||||
|
|
||||||
|
// Network Locations
|
||||||
|
Internet: status.SecurityLevelsDynamicAndSecure,
|
||||||
|
LAN: status.SecurityLevelsDynamicAndSecure,
|
||||||
|
Localhost: status.SecurityLevelsAll,
|
||||||
|
|
||||||
|
// Specials
|
||||||
|
Related: status.SecurityLevelDynamic,
|
||||||
|
PeerToPeer: status.SecurityLevelDynamic,
|
||||||
|
},
|
||||||
|
Ports: map[int16][]*Port{
|
||||||
|
6: []*Port{
|
||||||
|
&Port{ // SSH
|
||||||
|
Permit: true,
|
||||||
|
Created: time.Now().Unix(),
|
||||||
|
Start: 22,
|
||||||
|
End: 22,
|
||||||
|
},
|
||||||
|
&Port{ // HTTP
|
||||||
|
Permit: true,
|
||||||
|
Created: time.Now().Unix(),
|
||||||
|
Start: 80,
|
||||||
|
End: 80,
|
||||||
|
},
|
||||||
|
&Port{ // HTTPS
|
||||||
|
Permit: true,
|
||||||
|
Created: time.Now().Unix(),
|
||||||
|
Start: 443,
|
||||||
|
End: 443,
|
||||||
|
},
|
||||||
|
&Port{ // SMTP (TLS)
|
||||||
|
Permit: true,
|
||||||
|
Created: time.Now().Unix(),
|
||||||
|
Start: 465,
|
||||||
|
End: 465,
|
||||||
|
},
|
||||||
|
&Port{ // SMTP (STARTTLS)
|
||||||
|
Permit: true,
|
||||||
|
Created: time.Now().Unix(),
|
||||||
|
Start: 587,
|
||||||
|
End: 587,
|
||||||
|
},
|
||||||
|
&Port{ // IMAP (TLS)
|
||||||
|
Permit: true,
|
||||||
|
Created: time.Now().Unix(),
|
||||||
|
Start: 993,
|
||||||
|
End: 993,
|
||||||
|
},
|
||||||
|
&Port{ // IMAP (STARTTLS)
|
||||||
|
Permit: true,
|
||||||
|
Created: time.Now().Unix(),
|
||||||
|
Start: 143,
|
||||||
|
End: 143,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -63,14 +63,14 @@ func GetFingerprintWeight(fpType string) (weight int) {
|
|||||||
// return
|
// return
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// func (p *Profile) AddFingerprint(fp *Fingerprint) error {
|
func (p *Profile) AddFingerprint(fp *Fingerprint) {
|
||||||
// if fp.OS == "" {
|
if fp.OS == "" {
|
||||||
// fp.OS = osIdentifier
|
fp.OS = osIdentifier
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// p.Fingerprints = append(p.Fingerprints, fp)
|
p.Fingerprints = append(p.Fingerprints, fp)
|
||||||
// return p.Save()
|
}
|
||||||
// }
|
|
||||||
//
|
//
|
||||||
// func (p *Profile) GetApplicableFingerprintTypes() (types []string) {
|
// func (p *Profile) GetApplicableFingerprintTypes() (types []string) {
|
||||||
// for _, fp := range p.Fingerprints {
|
// for _, fp := range p.Fingerprints {
|
||||||
|
|||||||
47
profile/identifier_linux.go
Normal file
47
profile/identifier_linux.go
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
package profile
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/Safing/portbase/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetPathIdentifier returns the identifier from the given path
|
||||||
|
func GetPathIdentifier(path string) string {
|
||||||
|
// clean path
|
||||||
|
// TODO: is this necessary?
|
||||||
|
cleanedPath, err := filepath.EvalSymlinks(path)
|
||||||
|
if err == nil {
|
||||||
|
path = cleanedPath
|
||||||
|
} else {
|
||||||
|
path = filepath.Clean(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
splittedPath := strings.Split(path, "/")
|
||||||
|
|
||||||
|
// strip sensitive data
|
||||||
|
switch {
|
||||||
|
case strings.HasPrefix(path, "/home/"):
|
||||||
|
splittedPath = splittedPath[3:]
|
||||||
|
case strings.HasPrefix(path, "/root/"):
|
||||||
|
splittedPath = splittedPath[2:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// common directories with executable
|
||||||
|
if i := utils.IndexOfString(splittedPath, "bin"); i > 0 {
|
||||||
|
splittedPath = splittedPath[i:]
|
||||||
|
return strings.Join(splittedPath, "/")
|
||||||
|
}
|
||||||
|
if i := utils.IndexOfString(splittedPath, "sbin"); i > 0 {
|
||||||
|
splittedPath = splittedPath[i:]
|
||||||
|
return strings.Join(splittedPath, "/")
|
||||||
|
}
|
||||||
|
|
||||||
|
// shorten to max 3
|
||||||
|
if len(splittedPath) > 3 {
|
||||||
|
splittedPath = splittedPath[len(splittedPath)-3:]
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.Join(splittedPath, "/")
|
||||||
|
}
|
||||||
23
profile/identifier_linux_test.go
Normal file
23
profile/identifier_linux_test.go
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
package profile
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func testPathID(t *testing.T, execPath, identifierPath string) {
|
||||||
|
result := GetPathIdentifier(execPath)
|
||||||
|
if result != identifierPath {
|
||||||
|
t.Errorf("unexpected identifier path for %s: got %s, expected %s", execPath, result, identifierPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetPathIdentifier(t *testing.T) {
|
||||||
|
testPathID(t, "/bin/bash", "bin/bash")
|
||||||
|
testPathID(t, "/home/user/bin/bash", "bin/bash")
|
||||||
|
testPathID(t, "/home/user/project/main", "project/main")
|
||||||
|
testPathID(t, "/root/project/main", "project/main")
|
||||||
|
testPathID(t, "/tmp/a/b/c/d/install.sh", "c/d/install.sh")
|
||||||
|
testPathID(t, "/sbin/init", "sbin/init")
|
||||||
|
testPathID(t, "/lib/systemd/systemd-udevd", "lib/systemd/systemd-udevd")
|
||||||
|
testPathID(t, "/bundle/ruby/2.4.0/bin/passenger", "bin/passenger")
|
||||||
|
testPathID(t, "/usr/sbin/cron", "sbin/cron")
|
||||||
|
testPathID(t, "/usr/local/bin/python", "bin/python")
|
||||||
|
}
|
||||||
@@ -1,27 +1,26 @@
|
|||||||
package index
|
package index
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"sync"
|
|
||||||
"fmt"
|
|
||||||
"errors"
|
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/Safing/portbase/database/record"
|
"github.com/Safing/portbase/database/record"
|
||||||
"github.com/Safing/portbase/utils"
|
"github.com/Safing/portbase/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ProfileIndex links an Identifier to Profiles
|
// ProfileIndex links an Identifier to Profiles
|
||||||
type ProfileIndex struct {
|
type ProfileIndex struct {
|
||||||
record.Base
|
record.Base
|
||||||
sync.Mutex
|
sync.Mutex
|
||||||
|
|
||||||
ID string
|
UserProfiles []string
|
||||||
UserProfiles []string
|
StampProfiles []string
|
||||||
StampProfiles []string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeIndexRecordKey(id string) string {
|
func makeIndexRecordKey(id string) string {
|
||||||
return fmt.Sprintf("core:profiles/index/%s", base64.RawURLEncoding.EncodeToString([]byte(id)))
|
return fmt.Sprintf("index:profiles/%s", base64.RawURLEncoding.EncodeToString([]byte(id)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewIndex returns a new ProfileIndex.
|
// NewIndex returns a new ProfileIndex.
|
||||||
@@ -32,35 +31,35 @@ func NewIndex(id string) *ProfileIndex {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// AddUserProfile adds a User Profile to the index.
|
// AddUserProfile adds a User Profile to the index.
|
||||||
func (pi *ProfileIndex) AddUserProfile(id string) (changed bool) {
|
func (pi *ProfileIndex) AddUserProfile(identifier string) (changed bool) {
|
||||||
if !utils.StringInSlice(pi.UserProfiles, id) {
|
if !utils.StringInSlice(pi.UserProfiles, id) {
|
||||||
pi.UserProfiles = append(pi.UserProfiles, id)
|
pi.UserProfiles = append(pi.UserProfiles, id)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddStampProfile adds a Stamp Profile to the index.
|
// AddStampProfile adds a Stamp Profile to the index.
|
||||||
func (pi *ProfileIndex) AddStampProfile(id string) (changed bool) {
|
func (pi *ProfileIndex) AddStampProfile(identifier string) (changed bool) {
|
||||||
if !utils.StringInSlice(pi.StampProfiles, id) {
|
if !utils.StringInSlice(pi.StampProfiles, id) {
|
||||||
pi.StampProfiles = append(pi.StampProfiles, id)
|
pi.StampProfiles = append(pi.StampProfiles, id)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveUserProfile removes a profile from the index.
|
// RemoveUserProfile removes a profile from the index.
|
||||||
func (pi *ProfileIndex) RemoveUserProfile(id string) {
|
func (pi *ProfileIndex) RemoveUserProfile(id string) {
|
||||||
pi.UserProfiles = utils.RemoveFromStringSlice(pi.UserProfiles, id)
|
pi.UserProfiles = utils.RemoveFromStringSlice(pi.UserProfiles, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveStampProfile removes a profile from the index.
|
// RemoveStampProfile removes a profile from the index.
|
||||||
func (pi *ProfileIndex) RemoveStampProfile(id string) {
|
func (pi *ProfileIndex) RemoveStampProfile(id string) {
|
||||||
pi.StampProfiles = utils.RemoveFromStringSlice(pi.StampProfiles, id)
|
pi.StampProfiles = utils.RemoveFromStringSlice(pi.StampProfiles, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetIndex gets a ProfileIndex from the database.
|
// Get gets a ProfileIndex from the database.
|
||||||
func GetIndex(id string) (*ProfileIndex, error) {
|
func Get(id string) (*ProfileIndex, error) {
|
||||||
key := makeIndexRecordKey(id)
|
key := makeIndexRecordKey(id)
|
||||||
|
|
||||||
r, err := indexDB.Get(key)
|
r, err := indexDB.Get(key)
|
||||||
@@ -89,13 +88,13 @@ func GetIndex(id string) (*ProfileIndex, error) {
|
|||||||
|
|
||||||
// Save saves the Identifiers to the database
|
// Save saves the Identifiers to the database
|
||||||
func (pi *ProfileIndex) Save() error {
|
func (pi *ProfileIndex) Save() error {
|
||||||
if pi.Key() == "" {
|
if pi.Key() == "" {
|
||||||
if pi.ID != "" {
|
if pi.ID != "" {
|
||||||
pi.SetKey(makeIndexRecordKey(pi.ID))
|
pi.SetKey(makeIndexRecordKey(pi.ID))
|
||||||
} else {
|
} else {
|
||||||
return errors.New("missing identification Key")
|
return errors.New("missing identification Key")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return indexDB.Put(pi)
|
return indexDB.Put(pi)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package matcher
|
package matching
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/Safing/portbase/database"
|
"github.com/Safing/portbase/database"
|
||||||
3
profile/module.go
Normal file
3
profile/module.go
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
package profile
|
||||||
|
|
||||||
|
.
|
||||||
@@ -3,6 +3,7 @@ package profile
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
uuid "github.com/satori/go.uuid"
|
uuid "github.com/satori/go.uuid"
|
||||||
|
|
||||||
@@ -10,6 +11,10 @@ import (
|
|||||||
"github.com/Safing/portmaster/status"
|
"github.com/Safing/portmaster/status"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
lastUsedUpdateThreshold = 1 * time.Hour
|
||||||
|
)
|
||||||
|
|
||||||
// Profile is used to predefine a security profile for applications.
|
// Profile is used to predefine a security profile for applications.
|
||||||
type Profile struct {
|
type Profile struct {
|
||||||
record.Base
|
record.Base
|
||||||
@@ -24,7 +29,7 @@ type Profile struct {
|
|||||||
Icon string
|
Icon string
|
||||||
|
|
||||||
// Fingerprints
|
// Fingerprints
|
||||||
Fingerprints []string
|
Fingerprints []*Fingerprint
|
||||||
|
|
||||||
// The mininum security level to apply to connections made with this profile
|
// The mininum security level to apply to connections made with this profile
|
||||||
SecurityLevel uint8
|
SecurityLevel uint8
|
||||||
@@ -47,6 +52,10 @@ func New() *Profile {
|
|||||||
return &Profile{}
|
return &Profile{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func makeProfileKey(namespace, ID string) string {
|
||||||
|
return fmt.Sprintf("core:profiles/%s/%s", namespace, ID)
|
||||||
|
}
|
||||||
|
|
||||||
// Save saves the profile to the database
|
// Save saves the profile to the database
|
||||||
func (profile *Profile) Save(namespace string) error {
|
func (profile *Profile) Save(namespace string) error {
|
||||||
if profile.ID == "" {
|
if profile.ID == "" {
|
||||||
@@ -61,12 +70,21 @@ func (profile *Profile) Save(namespace string) error {
|
|||||||
if namespace == "" {
|
if namespace == "" {
|
||||||
return fmt.Errorf("no key or namespace defined for profile %s", profile.String())
|
return fmt.Errorf("no key or namespace defined for profile %s", profile.String())
|
||||||
}
|
}
|
||||||
profile.SetKey(fmt.Sprintf("config:profiles/%s/%s", namespace, profile.ID))
|
profile.SetKey(makeProfileKey(namespace, profile.ID))
|
||||||
}
|
}
|
||||||
|
|
||||||
return profileDB.Put(profile)
|
return profileDB.Put(profile)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MarkUsed marks the profile as used, eventually.
|
||||||
|
func (profile *Profile) MarkUsed() (updated bool) {
|
||||||
|
if time.Now().Add(-lastUsedUpdateThreshold).Unix() > profile.ApproxLastUsed {
|
||||||
|
profile.ApproxLastUsed = time.Now().Unix()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// String returns a string representation of the Profile.
|
// String returns a string representation of the Profile.
|
||||||
func (profile *Profile) String() string {
|
func (profile *Profile) String() string {
|
||||||
return profile.Name
|
return profile.Name
|
||||||
@@ -79,10 +97,38 @@ func (profile *Profile) DetailedString() string {
|
|||||||
|
|
||||||
// GetUserProfile loads a profile from the database.
|
// GetUserProfile loads a profile from the database.
|
||||||
func GetUserProfile(ID string) (*Profile, error) {
|
func GetUserProfile(ID string) (*Profile, error) {
|
||||||
return nil, nil
|
return getProfile(userNamespace, ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetStampProfile loads a profile from the database.
|
// GetStampProfile loads a profile from the database.
|
||||||
func GetStampProfile(ID string) (*Profile, error) {
|
func GetStampProfile(ID string) (*Profile, error) {
|
||||||
return nil, nil
|
return getProfile(stampNamespace, ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getProfile(namespace, ID string) (*Profile, error) {
|
||||||
|
r, err := profileDB.Get(makeProfileKey(namespace, ID))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return ensureProfile(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ensureProfile(r record.Record) (*Profile, error) {
|
||||||
|
// unwrap
|
||||||
|
if r.IsWrapped() {
|
||||||
|
// only allocate a new struct, if we need it
|
||||||
|
new := &Profile{}
|
||||||
|
err := record.Unwrap(r, new)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return new, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// or adjust type
|
||||||
|
new, ok := r.(*Profile)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("record not of type *Example, but %T", r)
|
||||||
|
}
|
||||||
|
return new, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
package profile
|
package profile
|
||||||
|
|
||||||
import "github.com/Safing/portmaster/status"
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/Safing/portmaster/status"
|
||||||
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
emptyFlags = Flags{}
|
emptyFlags = Flags{}
|
||||||
@@ -9,6 +13,8 @@ var (
|
|||||||
|
|
||||||
// Set handles Profile chaining.
|
// Set handles Profile chaining.
|
||||||
type Set struct {
|
type Set struct {
|
||||||
|
sync.Mutex
|
||||||
|
|
||||||
profiles [4]*Profile
|
profiles [4]*Profile
|
||||||
// Application
|
// Application
|
||||||
// Global
|
// Global
|
||||||
@@ -29,6 +35,7 @@ func NewSet(user, stamp *Profile) *Set {
|
|||||||
nil, // Default
|
nil, // Default
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
activateProfileSet(new)
|
||||||
new.Update(status.SecurityLevelFortress)
|
new.Update(status.SecurityLevelFortress)
|
||||||
return new
|
return new
|
||||||
}
|
}
|
||||||
@@ -40,7 +47,7 @@ func (set *Set) Update(securityLevel uint8) {
|
|||||||
|
|
||||||
// update profiles
|
// update profiles
|
||||||
set.profiles[1] = globalProfile
|
set.profiles[1] = globalProfile
|
||||||
set.profiles[3] = defaultProfile
|
set.profiles[3] = fallbackProfile
|
||||||
|
|
||||||
// update security level
|
// update security level
|
||||||
profileSecurityLevel := set.getProfileSecurityLevel()
|
profileSecurityLevel := set.getProfileSecurityLevel()
|
||||||
@@ -96,7 +103,7 @@ func (set *Set) CheckDomain(domain string) (permit, ok bool) {
|
|||||||
return false, false
|
return false, false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ports returns the highest prioritized Ports configuration.
|
// CheckPort checks if the given protocol and port are governed in any the lists of ports and returns whether it is permitted.
|
||||||
func (set *Set) CheckPort(listen bool, protocol uint8, port uint16) (permit, ok bool) {
|
func (set *Set) CheckPort(listen bool, protocol uint8, port uint16) (permit, ok bool) {
|
||||||
|
|
||||||
signedProtocol := int16(protocol)
|
signedProtocol := int16(protocol)
|
||||||
|
|||||||
@@ -1,261 +0,0 @@
|
|||||||
package profile
|
|
||||||
|
|
||||||
// DEACTIVATED
|
|
||||||
|
|
||||||
// import (
|
|
||||||
// "runtime"
|
|
||||||
//
|
|
||||||
// "github.com/Safing/portbase/database"
|
|
||||||
// "github.com/Safing/portbase/log"
|
|
||||||
// )
|
|
||||||
//
|
|
||||||
// func init() {
|
|
||||||
//
|
|
||||||
// // Data here is for demo purposes, Profiles will be served over network soon™.
|
|
||||||
//
|
|
||||||
// log.Tracef("profiles: loading sample profiles for %s", runtime.GOOS)
|
|
||||||
//
|
|
||||||
// switch runtime.GOOS {
|
|
||||||
// case "linux":
|
|
||||||
//
|
|
||||||
// log.Trace("profiles: loading linux sample profiles")
|
|
||||||
//
|
|
||||||
// (&Profile{
|
|
||||||
// Name: "Chromium",
|
|
||||||
// Description: "Browser by Google",
|
|
||||||
// Path: "/usr/lib/chromium-browser/chromium-browser",
|
|
||||||
// Flags: []int8{User, Internet, LocalNet, Browser},
|
|
||||||
// ConnectPorts: []uint16{80, 443},
|
|
||||||
// }).CreateInDist()
|
|
||||||
//
|
|
||||||
// (&Profile{
|
|
||||||
// Name: "Evolution",
|
|
||||||
// Description: "PIM solution by GNOME",
|
|
||||||
// Path: "/usr/bin/evolution",
|
|
||||||
// Flags: []int8{User, Internet, Gateway},
|
|
||||||
// ConnectPorts: []uint16{25, 80, 143, 443, 465, 587, 993, 995},
|
|
||||||
// SecurityLevel: 2,
|
|
||||||
// }).CreateInDist()
|
|
||||||
//
|
|
||||||
// (&Profile{
|
|
||||||
// Name: "Evolution Calendar",
|
|
||||||
// Description: "PIM solution by GNOME - Calendar",
|
|
||||||
// Path: "/usr/lib/evolution/evolution-calendar-factory-subprocess",
|
|
||||||
// Flags: []int8{User, Internet, Gateway},
|
|
||||||
// ConnectPorts: []uint16{80, 443},
|
|
||||||
// SecurityLevel: 2,
|
|
||||||
// }).CreateInDist()
|
|
||||||
//
|
|
||||||
// (&Profile{
|
|
||||||
// Name: "Spotify",
|
|
||||||
// Description: "Music streaming",
|
|
||||||
// Path: "/usr/share/spotify/spotify",
|
|
||||||
// ConnectPorts: []uint16{80, 443, 4070},
|
|
||||||
// Flags: []int8{User, Internet, Strict},
|
|
||||||
// }).CreateInDist()
|
|
||||||
//
|
|
||||||
// (&Profile{
|
|
||||||
// // flatpak edition
|
|
||||||
// Name: "Spotify",
|
|
||||||
// Description: "Music streaming",
|
|
||||||
// Path: "/newroot/app/extra/share/spotify/spotify",
|
|
||||||
// ConnectPorts: []uint16{80, 443, 4070},
|
|
||||||
// Flags: []int8{User, Internet, Strict},
|
|
||||||
// }).CreateInDist()
|
|
||||||
//
|
|
||||||
// (&Profile{
|
|
||||||
// Name: "Evince",
|
|
||||||
// Description: "PDF Document Reader",
|
|
||||||
// Path: "/usr/bin/evince",
|
|
||||||
// Flags: []int8{},
|
|
||||||
// SecurityLevel: 2,
|
|
||||||
// }).CreateInDist()
|
|
||||||
//
|
|
||||||
// (&Profile{
|
|
||||||
// Name: "Ahavi",
|
|
||||||
// Description: "mDNS service",
|
|
||||||
// Path: "/usr/bin/avahi-daemon",
|
|
||||||
// Flags: []int8{System, LocalNet, Service, Directconnect},
|
|
||||||
// }).CreateInDist()
|
|
||||||
//
|
|
||||||
// (&Profile{
|
|
||||||
// Name: "Python 2.7 Framework",
|
|
||||||
// Description: "Correctly handle python scripts",
|
|
||||||
// Path: "/usr/bin/python2.7",
|
|
||||||
// Framework: &Framework{
|
|
||||||
// Find: "^[^ ]+ ([^ ]+)",
|
|
||||||
// Build: "{1}|{CWD}/{1}",
|
|
||||||
// },
|
|
||||||
// }).CreateInDist()
|
|
||||||
//
|
|
||||||
// (&Profile{
|
|
||||||
// Name: "Python 3.5 Framework",
|
|
||||||
// Description: "Correctly handle python scripts",
|
|
||||||
// Path: "/usr/bin/python3.5",
|
|
||||||
// Framework: &Framework{
|
|
||||||
// Find: "^[^ ]+ ([^ ]+)",
|
|
||||||
// Build: "{1}|{CWD}/{1}",
|
|
||||||
// },
|
|
||||||
// }).CreateInDist()
|
|
||||||
//
|
|
||||||
// (&Profile{
|
|
||||||
// Name: "DHCP Client",
|
|
||||||
// Description: "Client software for the DHCP protocol",
|
|
||||||
// Path: "/sbin/dhclient",
|
|
||||||
// Framework: &Framework{
|
|
||||||
// FindParent: 1,
|
|
||||||
// MergeWithParent: true,
|
|
||||||
// },
|
|
||||||
// }).CreateInDist()
|
|
||||||
//
|
|
||||||
// // Default Profiles
|
|
||||||
// // Until Profiles are distributed over the network, default profiles are activated when the Default Profile for "/" is missing.
|
|
||||||
//
|
|
||||||
// if ok, err := database.Has(ds.NewKey("/Data/Profiles/Profile_d-2f")); !ok || err != nil {
|
|
||||||
//
|
|
||||||
// log.Trace("profiles: loading linux default sample profiles")
|
|
||||||
//
|
|
||||||
// (&Profile{
|
|
||||||
// Name: "Default Base",
|
|
||||||
// Description: "Default Profile for /",
|
|
||||||
// Path: "/",
|
|
||||||
// Flags: []int8{Internet, LocalNet, Strict},
|
|
||||||
// Default: true,
|
|
||||||
// }).Create()
|
|
||||||
//
|
|
||||||
// (&Profile{
|
|
||||||
// Name: "Installed Applications",
|
|
||||||
// Description: "Default Profile for /usr/bin",
|
|
||||||
// Path: "/usr/bin/",
|
|
||||||
// Flags: []int8{Internet, LocalNet, Gateway},
|
|
||||||
// Default: true,
|
|
||||||
// }).Create()
|
|
||||||
//
|
|
||||||
// (&Profile{
|
|
||||||
// Name: "System Binaries (/sbin)",
|
|
||||||
// Description: "Default Profile for ~/Downloads",
|
|
||||||
// Path: "/sbin/",
|
|
||||||
// Flags: []int8{Internet, LocalNet, Directconnect, Service, System},
|
|
||||||
// Default: true,
|
|
||||||
// }).Create()
|
|
||||||
//
|
|
||||||
// (&Profile{
|
|
||||||
// Name: "System Binaries (/usr/sbin)",
|
|
||||||
// Description: "Default Profile for ~/Downloads",
|
|
||||||
// Path: "/usr/sbin/",
|
|
||||||
// Flags: []int8{Internet, LocalNet, Directconnect, Service, System},
|
|
||||||
// Default: true,
|
|
||||||
// }).Create()
|
|
||||||
//
|
|
||||||
// (&Profile{
|
|
||||||
// Name: "System Tmp folder",
|
|
||||||
// Description: "Default Profile for /tmp",
|
|
||||||
// Path: "/tmp/",
|
|
||||||
// Flags: []int8{}, // deny all
|
|
||||||
// Default: true,
|
|
||||||
// }).Create()
|
|
||||||
//
|
|
||||||
// (&Profile{
|
|
||||||
// Name: "User Home",
|
|
||||||
// Description: "Default Profile for ~/",
|
|
||||||
// Path: "~/",
|
|
||||||
// Flags: []int8{Internet, LocalNet, Gateway},
|
|
||||||
// Default: true,
|
|
||||||
// }).Create()
|
|
||||||
//
|
|
||||||
// (&Profile{
|
|
||||||
// Name: "User Downloads",
|
|
||||||
// Description: "Default Profile for ~/Downloads",
|
|
||||||
// Path: "~/Downloads/",
|
|
||||||
// Flags: []int8{}, // deny all
|
|
||||||
// Default: true,
|
|
||||||
// }).Create()
|
|
||||||
//
|
|
||||||
// (&Profile{
|
|
||||||
// Name: "User Cache",
|
|
||||||
// Description: "Default Profile for ~/.cache",
|
|
||||||
// Path: "~/.cache/",
|
|
||||||
// Flags: []int8{}, // deny all
|
|
||||||
// Default: true,
|
|
||||||
// }).Create()
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// case "windows":
|
|
||||||
//
|
|
||||||
// log.Trace("profiles: loading windows sample profiles")
|
|
||||||
//
|
|
||||||
// (&Profile{
|
|
||||||
// Name: "Firefox",
|
|
||||||
// Description: "Firefox Browser by Mozilla",
|
|
||||||
// Path: "C:\\Program Files\\Mozilla Firefox\\firefox.exe",
|
|
||||||
// Flags: []int8{User, Internet, LocalNet, Browser},
|
|
||||||
// ConnectPorts: []uint16{80, 443},
|
|
||||||
// }).CreateInDist()
|
|
||||||
//
|
|
||||||
// // Default Profiles
|
|
||||||
// // Until Profiles are distributed over the network, default profiles are activated when the Default Profile for "C" is missing.
|
|
||||||
//
|
|
||||||
// if ok, err := database.Has(ds.NewKey("/Data/Profiles/Profile:d-C")); !ok || err != nil {
|
|
||||||
//
|
|
||||||
// log.Trace("profiles: loading windows default sample profiles")
|
|
||||||
//
|
|
||||||
// (&Profile{
|
|
||||||
// Name: "Default Base",
|
|
||||||
// Description: "Default Profile for C",
|
|
||||||
// Path: "C",
|
|
||||||
// Flags: []int8{Internet, LocalNet, Strict},
|
|
||||||
// Default: true,
|
|
||||||
// }).Create()
|
|
||||||
//
|
|
||||||
// (&Profile{
|
|
||||||
// Name: "Installed Applications",
|
|
||||||
// Description: "Default Profile for C:\\Program Files",
|
|
||||||
// Path: "C:\\Program Files\\",
|
|
||||||
// Flags: []int8{Internet, LocalNet, Gateway},
|
|
||||||
// Default: true,
|
|
||||||
// }).Create()
|
|
||||||
//
|
|
||||||
// (&Profile{
|
|
||||||
// Name: "Installed Applications (x86)",
|
|
||||||
// Description: "Default Profile for C:\\Program Files (x86)",
|
|
||||||
// Path: "C:\\Program Files (x86)\\",
|
|
||||||
// Flags: []int8{Internet, LocalNet, Gateway},
|
|
||||||
// Default: true,
|
|
||||||
// }).Create()
|
|
||||||
//
|
|
||||||
// (&Profile{
|
|
||||||
// Name: "System Applications (C:\\Windows\\System32)",
|
|
||||||
// Description: "Default Profile for C:\\Windows\\System32",
|
|
||||||
// Path: "C:\\Windows\\System32\\",
|
|
||||||
// Flags: []int8{Internet, LocalNet, Directconnect, Service, System},
|
|
||||||
// Default: true,
|
|
||||||
// }).Create()
|
|
||||||
//
|
|
||||||
// (&Profile{
|
|
||||||
// Name: "User Home",
|
|
||||||
// Description: "Default Profile for ~/",
|
|
||||||
// Path: "~/",
|
|
||||||
// Flags: []int8{Internet, LocalNet, Gateway},
|
|
||||||
// Default: true,
|
|
||||||
// }).Create()
|
|
||||||
//
|
|
||||||
// (&Profile{
|
|
||||||
// Name: "User Downloads",
|
|
||||||
// Description: "Default Profile for ~/Downloads",
|
|
||||||
// Path: "~/Downloads/",
|
|
||||||
// Flags: []int8{}, // deny all
|
|
||||||
// Default: true,
|
|
||||||
// }).Create()
|
|
||||||
//
|
|
||||||
// (&Profile{
|
|
||||||
// Name: "User Cache",
|
|
||||||
// Description: "Default Profile for ~/.cache",
|
|
||||||
// Path: "~/.cache/",
|
|
||||||
// Flags: []int8{}, // deny all
|
|
||||||
// Default: true,
|
|
||||||
// }).Create()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
@@ -1,12 +1,44 @@
|
|||||||
package profile
|
package profile
|
||||||
|
|
||||||
import "sync"
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/Safing/portbase/database"
|
||||||
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
globalProfile *Profile
|
globalProfile *Profile
|
||||||
defaultProfile *Profile
|
fallbackProfile *Profile
|
||||||
|
|
||||||
specialProfileLock sync.RWMutex
|
specialProfileLock sync.RWMutex
|
||||||
)
|
)
|
||||||
|
|
||||||
// FIXME: subscribe to changes and update profiles
|
func initSpecialProfiles() (err error) {
|
||||||
|
|
||||||
|
specialProfileLock.Lock()
|
||||||
|
defer specialProfileLock.Unlock()
|
||||||
|
|
||||||
|
globalProfile, err = getSpecialProfile("global")
|
||||||
|
if err != nil {
|
||||||
|
if err != database.ErrNotFound {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
globalProfile = makeDefaultGlobalProfile()
|
||||||
|
globalProfile.Save(specialNamespace)
|
||||||
|
}
|
||||||
|
|
||||||
|
fallbackProfile, err = getSpecialProfile("fallback")
|
||||||
|
if err != nil {
|
||||||
|
if err != database.ErrNotFound {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fallbackProfile = makeDefaultFallbackProfile()
|
||||||
|
fallbackProfile.Save(specialNamespace)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSpecialProfile(ID string) (*Profile, error) {
|
||||||
|
return getProfile(specialNamespace, ID)
|
||||||
|
}
|
||||||
|
|||||||
53
profile/updates.go
Normal file
53
profile/updates.go
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
package profile
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/Safing/portbase/database"
|
||||||
|
"github.com/Safing/portbase/database/query"
|
||||||
|
"github.com/Safing/portbase/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func initUpdateListener() error {
|
||||||
|
sub, err := profileDB.Subscribe(query.New(makeProfileKey(specialNamespace, "")))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
go updateListener(sub)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
slashedUserNamespace = fmt.Sprintf("/%s/", userNamespace)
|
||||||
|
slashedStampNamespace = fmt.Sprintf("/%s/", stampNamespace)
|
||||||
|
)
|
||||||
|
|
||||||
|
func updateListener(sub *database.Subscription) {
|
||||||
|
for r := range sub.Feed {
|
||||||
|
profile, err := ensureProfile(r)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("profile: received update for special profile, but could not read: %s", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
specialProfileLock.Lock()
|
||||||
|
switch profile.ID {
|
||||||
|
case "global":
|
||||||
|
globalProfile = profile
|
||||||
|
updateActiveGlobalProfile(profile)
|
||||||
|
case "fallback":
|
||||||
|
fallbackProfile = profile
|
||||||
|
updateActiveFallbackProfile(profile)
|
||||||
|
default:
|
||||||
|
switch {
|
||||||
|
case strings.HasPrefix(profile.Key(), makeProfileKey(userNamespace, "")):
|
||||||
|
updateActiveUserProfile(profile)
|
||||||
|
case strings.HasPrefix(profile.Key(), makeProfileKey(stampNamespace, "")):
|
||||||
|
updateActiveStampProfile(profile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
specialProfileLock.Unlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user