Compare commits

...

5 Commits

Author SHA1 Message Date
ba07625b7c v0.0.54 2022-12-29 22:52:52 +01:00
aeded3fb37 v0.0.53 2022-12-24 03:11:09 +01:00
1a1cd6d0aa v0.0.52 2022-12-24 02:50:46 +01:00
64cc1342a0 v0.0.51 2022-12-24 01:14:58 +01:00
8431b6adf5 v0.0.50 2022-12-23 20:08:59 +01:00
4 changed files with 263 additions and 16 deletions

142
cmdext/cmdrunner.go Normal file
View File

@@ -0,0 +1,142 @@
package cmdext
import (
"bufio"
"os/exec"
"time"
)
type CommandResult struct {
StdOut string
StdErr string
StdCombined string
ExitCode int
CommandTimedOut bool
}
func RunCommand(program string, args []string, timeout *time.Duration) (CommandResult, error) {
cmd := exec.Command(program, args...)
stdoutPipe, err := cmd.StdoutPipe()
if err != nil {
return CommandResult{}, err
}
stderrPipe, err := cmd.StderrPipe()
if err != nil {
return CommandResult{}, err
}
err = cmd.Start()
if err != nil {
return CommandResult{}, err
}
errch := make(chan error, 1)
go func() { errch <- cmd.Wait() }()
combch := make(chan string, 32)
stopCombch := make(chan bool)
stdout := ""
go func() {
scanner := bufio.NewScanner(stdoutPipe)
for scanner.Scan() {
txt := scanner.Text()
stdout += txt
combch <- txt
}
}()
stderr := ""
go func() {
scanner := bufio.NewScanner(stderrPipe)
for scanner.Scan() {
txt := scanner.Text()
stderr += txt
combch <- txt
}
}()
defer func() {
stopCombch <- true
}()
stdcombined := ""
go func() {
for {
select {
case txt := <-combch:
stdcombined += txt
case <-stopCombch:
return
}
}
}()
if timeout != nil {
select {
case <-time.After(*timeout):
_ = cmd.Process.Kill()
return CommandResult{
StdOut: stdout,
StdErr: stderr,
StdCombined: stdcombined,
ExitCode: -1,
CommandTimedOut: true,
}, nil
case err := <-errch:
if exiterr, ok := err.(*exec.ExitError); ok {
return CommandResult{
StdOut: stdout,
StdErr: stderr,
StdCombined: stdcombined,
ExitCode: exiterr.ExitCode(),
CommandTimedOut: false,
}, nil
} else if err != nil {
return CommandResult{}, err
} else {
return CommandResult{
StdOut: stdout,
StdErr: stderr,
StdCombined: stdcombined,
ExitCode: 0,
CommandTimedOut: false,
}, nil
}
}
} else {
select {
case err := <-errch:
if exiterr, ok := err.(*exec.ExitError); ok {
return CommandResult{
StdOut: stdout,
StdErr: stderr,
StdCombined: stdcombined,
ExitCode: exiterr.ExitCode(),
CommandTimedOut: false,
}, nil
} else if err != nil {
return CommandResult{}, err
} else {
return CommandResult{
StdOut: stdout,
StdErr: stderr,
StdCombined: stdcombined,
ExitCode: 0,
CommandTimedOut: false,
}, nil
}
}
}
}

View File

@@ -179,6 +179,101 @@ func (ph PassHash) Upgrade(plainpass string) (PassHash, error) {
}
}
func (ph PassHash) ClearTOTP() (PassHash, error) {
version, _, _, _, _, valid := ph.Data()
if !valid {
return "", errors.New("invalid PassHash")
}
if version == 0 {
return ph, nil
}
if version == 1 {
return ph, nil
}
if version == 2 {
return ph, nil
}
if version == 3 {
split := strings.Split(string(ph), "|")
split[3] = "0"
return PassHash(strings.Join(split, "|")), nil
}
if version == 4 {
split := strings.Split(string(ph), "|")
split[2] = "0"
return PassHash(strings.Join(split, "|")), nil
}
return "", errors.New("unknown version")
}
func (ph PassHash) WithTOTP(totpSecret []byte) (PassHash, error) {
version, _, _, _, _, valid := ph.Data()
if !valid {
return "", errors.New("invalid PassHash")
}
if version == 0 {
return "", errors.New("version does not support totp, needs upgrade")
}
if version == 1 {
return "", errors.New("version does not support totp, needs upgrade")
}
if version == 2 {
return "", errors.New("version does not support totp, needs upgrade")
}
if version == 3 {
split := strings.Split(string(ph), "|")
split[3] = hex.EncodeToString(totpSecret)
return PassHash(strings.Join(split, "|")), nil
}
if version == 4 {
split := strings.Split(string(ph), "|")
split[2] = hex.EncodeToString(totpSecret)
return PassHash(strings.Join(split, "|")), nil
}
return "", errors.New("unknown version")
}
func (ph PassHash) Change(newPlainPass string) (PassHash, error) {
version, _, _, hastotp, totpsecret, valid := ph.Data()
if !valid {
return "", errors.New("invalid PassHash")
}
if version == 0 {
return HashPasswordV0(newPlainPass)
}
if version == 1 {
return HashPasswordV1(newPlainPass)
}
if version == 2 {
return HashPasswordV2(newPlainPass)
}
if version == 3 {
return HashPasswordV3(newPlainPass, langext.Conditional(hastotp, totpsecret, nil))
}
if version == 4 {
return HashPasswordV4(newPlainPass, langext.Conditional(hastotp, totpsecret, nil))
}
return "", errors.New("unknown version")
}
func (ph PassHash) String() string {
return string(ph)
}

View File

@@ -19,38 +19,38 @@ import (
// There are also a bunch of unit tests to ensure that the cache is always in a consistent state
//
type LRUMap[TData any] struct {
type LRUMap[TKey comparable, TData any] struct {
maxsize int
lock sync.Mutex
cache map[string]*cacheNode[TData]
cache map[TKey]*cacheNode[TKey, TData]
lfuHead *cacheNode[TData]
lfuTail *cacheNode[TData]
lfuHead *cacheNode[TKey, TData]
lfuTail *cacheNode[TKey, TData]
}
type cacheNode[TData any] struct {
key string
type cacheNode[TKey comparable, TData any] struct {
key TKey
data TData
parent *cacheNode[TData]
child *cacheNode[TData]
parent *cacheNode[TKey, TData]
child *cacheNode[TKey, TData]
}
func NewLRUMap[TData any](size int) *LRUMap[TData] {
func NewLRUMap[TKey comparable, TData any](size int) *LRUMap[TKey, TData] {
if size <= 2 && size != 0 {
panic("Size must be > 2 (or 0)")
}
return &LRUMap[TData]{
return &LRUMap[TKey, TData]{
maxsize: size,
lock: sync.Mutex{},
cache: make(map[string]*cacheNode[TData], size+1),
cache: make(map[TKey]*cacheNode[TKey, TData], size+1),
lfuHead: nil,
lfuTail: nil,
}
}
func (c *LRUMap[TData]) Put(key string, value TData) {
func (c *LRUMap[TKey, TData]) Put(key TKey, value TData) {
if c.maxsize == 0 {
return // cache disabled
}
@@ -68,7 +68,7 @@ func (c *LRUMap[TData]) Put(key string, value TData) {
}
// key does not exist: insert into map and add to top of LFU
node = &cacheNode[TData]{
node = &cacheNode[TKey, TData]{
key: key,
data: value,
parent: nil,
@@ -93,7 +93,7 @@ func (c *LRUMap[TData]) Put(key string, value TData) {
}
}
func (c *LRUMap[TData]) TryGet(key string) (TData, bool) {
func (c *LRUMap[TKey, TData]) TryGet(key TKey) (TData, bool) {
if c.maxsize == 0 {
return *new(TData), false // cache disabled
}
@@ -109,7 +109,7 @@ func (c *LRUMap[TData]) TryGet(key string) (TData, bool) {
return val.data, ok
}
func (c *LRUMap[TData]) moveNodeToTop(node *cacheNode[TData]) {
func (c *LRUMap[TKey, TData]) moveNodeToTop(node *cacheNode[TKey, TData]) {
// (only called in critical section !)
if c.lfuHead == node { // fast case
@@ -142,7 +142,7 @@ func (c *LRUMap[TData]) moveNodeToTop(node *cacheNode[TData]) {
}
}
func (c *LRUMap[TData]) Size() int {
func (c *LRUMap[TKey, TData]) Size() int {
c.lock.Lock()
defer c.lock.Unlock()
return len(c.cache)

View File

@@ -1,3 +1,13 @@
package sq
type PP map[string]any
func Join(pps ...PP) PP {
r := PP{}
for _, add := range pps {
for k, v := range add {
r[k] = v
}
}
return r
}