feat(files): implemented file permission promises
This commit is contained in:
parent
d7aa6c514b
commit
c1f22b97e6
3 changed files with 154 additions and 26 deletions
|
@ -20,20 +20,22 @@ func init() {
|
||||||
|
|
||||||
// ----- Public ----------------------------------------------------------------
|
// ----- Public ----------------------------------------------------------------
|
||||||
type FilePromise struct {
|
type FilePromise struct {
|
||||||
chain []Promise
|
chain []Promise
|
||||||
contents Value
|
contents Value
|
||||||
err error
|
err error
|
||||||
filename Value
|
filename Value
|
||||||
status Status
|
permissions *Permissions
|
||||||
|
status Status
|
||||||
}
|
}
|
||||||
|
|
||||||
func File(filename any) *FilePromise {
|
func File(filename any) *FilePromise {
|
||||||
return &FilePromise{
|
return &FilePromise{
|
||||||
chain: nil,
|
chain: nil,
|
||||||
contents: nil,
|
contents: nil,
|
||||||
err: nil,
|
err: nil,
|
||||||
filename: interfaceToTemplateValue(filename),
|
filename: interfaceToTemplateValue(filename),
|
||||||
status: PROMISED,
|
permissions: nil,
|
||||||
|
status: PROMISED,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,6 +44,11 @@ func (f *FilePromise) Contents(contents any) *FilePromise {
|
||||||
return f
|
return f
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *FilePromise) Permissions(p *Permissions) *FilePromise {
|
||||||
|
f.permissions = p
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
func (f *FilePromise) Template(contents any) *FilePromise {
|
func (f *FilePromise) Template(contents any) *FilePromise {
|
||||||
f.contents = interfaceToTemplateValue(contents)
|
f.contents = interfaceToTemplateValue(contents)
|
||||||
return f
|
return f
|
||||||
|
@ -79,16 +86,28 @@ func (f *FilePromise) Resolve() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
f.status = REPAIRED
|
f.status = REPAIRED
|
||||||
slog.Info("file", "filename", f.filename, "status", f.status)
|
|
||||||
for _, p := range f.chain {
|
|
||||||
p.Resolve()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if f.status == PROMISED {
|
if f.permissions != nil {
|
||||||
f.status = KEPT
|
var status Status
|
||||||
|
status, f.err = f.permissions.resolve(filename)
|
||||||
|
if f.status == PROMISED || status == BROKEN {
|
||||||
|
f.status = status
|
||||||
|
}
|
||||||
|
if f.err != nil {
|
||||||
|
slog.Error("file", "filename", f.filename, "status", f.status, "error", f.err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if f.status == REPAIRED {
|
||||||
|
slog.Info("file", "filename", f.filename, "status", f.status)
|
||||||
|
for _, p := range f.chain {
|
||||||
|
p.Resolve()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
f.status = KEPT
|
||||||
|
slog.Debug("file", "filename", f.filename, "status", f.status)
|
||||||
}
|
}
|
||||||
slog.Debug("file", "filename", f.filename, "status", f.status)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----- Internal --------------------------------------------------------------
|
// ----- Internal --------------------------------------------------------------
|
||||||
|
|
89
gonf/permissions.go
Normal file
89
gonf/permissions.go
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
package gonf
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io/fs"
|
||||||
|
"os"
|
||||||
|
"os/user"
|
||||||
|
"strconv"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Permissions struct {
|
||||||
|
group Value
|
||||||
|
mode Value
|
||||||
|
user Value
|
||||||
|
}
|
||||||
|
|
||||||
|
func ModeUserGroup(mode, user, group interface{}) *Permissions {
|
||||||
|
return &Permissions{
|
||||||
|
group: interfaceToTemplateValue(group),
|
||||||
|
mode: interfaceToTemplateValue(mode),
|
||||||
|
user: interfaceToTemplateValue(user),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Permissions) resolve(filename string) (Status, error) {
|
||||||
|
g, ok := p.group.(*IntValue)
|
||||||
|
if !ok {
|
||||||
|
if group, err := user.LookupGroup(p.group.String()); err != nil {
|
||||||
|
return BROKEN, err
|
||||||
|
} else {
|
||||||
|
if groupId, err := strconv.Atoi(group.Gid); err != nil {
|
||||||
|
return BROKEN, err
|
||||||
|
} else {
|
||||||
|
g = &IntValue{groupId}
|
||||||
|
p.group = g
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m, ok := p.mode.(*IntValue)
|
||||||
|
if !ok {
|
||||||
|
if i, err := strconv.Atoi(p.mode.String()); err != nil {
|
||||||
|
return BROKEN, err
|
||||||
|
} else {
|
||||||
|
m = &IntValue{i}
|
||||||
|
p.mode = m
|
||||||
|
}
|
||||||
|
}
|
||||||
|
u, ok := p.user.(*IntValue)
|
||||||
|
if !ok {
|
||||||
|
if user, err := user.Lookup(p.user.String()); err != nil {
|
||||||
|
return BROKEN, err
|
||||||
|
} else {
|
||||||
|
if userId, err := strconv.Atoi(user.Uid); err != nil {
|
||||||
|
return BROKEN, err
|
||||||
|
} else {
|
||||||
|
u = &IntValue{userId}
|
||||||
|
p.group = u
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var status Status = KEPT
|
||||||
|
if fileInfo, err := os.Lstat(filename); err != nil {
|
||||||
|
return BROKEN, err
|
||||||
|
} else {
|
||||||
|
gv := g.Int()
|
||||||
|
mv := fs.FileMode(m.Int())
|
||||||
|
uv := u.Int()
|
||||||
|
if fileInfo.Mode() != mv {
|
||||||
|
if err := os.Chmod(filename, mv); err != nil {
|
||||||
|
return BROKEN, err
|
||||||
|
}
|
||||||
|
status = REPAIRED
|
||||||
|
}
|
||||||
|
if stat, ok := fileInfo.Sys().(*syscall.Stat_t); ok {
|
||||||
|
if stat.Gid != uint32(gv) || stat.Uid != uint32(uv) {
|
||||||
|
if err := os.Chown(filename, uv, gv); err != nil {
|
||||||
|
return BROKEN, err
|
||||||
|
}
|
||||||
|
status = REPAIRED
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return BROKEN, errors.New("Unsupported operating system")
|
||||||
|
}
|
||||||
|
_ = gv
|
||||||
|
_ = uv
|
||||||
|
}
|
||||||
|
return status, nil
|
||||||
|
}
|
|
@ -11,12 +11,15 @@ type Value interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
func interfaceToValue(v any) Value {
|
func interfaceToValue(v any) Value {
|
||||||
if vv, ok := v.(string); ok {
|
|
||||||
return &StringValue{vv}
|
|
||||||
}
|
|
||||||
if vv, ok := v.([]byte); ok {
|
if vv, ok := v.([]byte); ok {
|
||||||
return &BytesValue{vv}
|
return &BytesValue{vv}
|
||||||
}
|
}
|
||||||
|
if vv, ok := v.(int); ok {
|
||||||
|
return &IntValue{vv}
|
||||||
|
}
|
||||||
|
if vv, ok := v.(string); ok {
|
||||||
|
return &StringValue{vv}
|
||||||
|
}
|
||||||
if vv, ok := v.(*VariablePromise); ok {
|
if vv, ok := v.(*VariablePromise); ok {
|
||||||
return vv
|
return vv
|
||||||
}
|
}
|
||||||
|
@ -25,12 +28,15 @@ func interfaceToValue(v any) Value {
|
||||||
}
|
}
|
||||||
|
|
||||||
func interfaceToTemplateValue(v any) Value {
|
func interfaceToTemplateValue(v any) Value {
|
||||||
if vv, ok := v.(string); ok {
|
|
||||||
return &TemplateValue{data: vv}
|
|
||||||
}
|
|
||||||
if vv, ok := v.([]byte); ok {
|
if vv, ok := v.([]byte); ok {
|
||||||
return &TemplateValue{data: string(vv)}
|
return &TemplateValue{data: string(vv)}
|
||||||
}
|
}
|
||||||
|
if vv, ok := v.(int); ok {
|
||||||
|
return &IntValue{vv}
|
||||||
|
}
|
||||||
|
if vv, ok := v.(string); ok {
|
||||||
|
return &TemplateValue{data: vv}
|
||||||
|
}
|
||||||
if vv, ok := v.(*VariablePromise); ok {
|
if vv, ok := v.(*VariablePromise); ok {
|
||||||
return vv
|
return vv
|
||||||
}
|
}
|
||||||
|
@ -38,7 +44,7 @@ func interfaceToTemplateValue(v any) Value {
|
||||||
panic(fmt.Sprintf("interfaceToTemplateValue cannot take type %T as argument. Value was %#v.", v, v))
|
panic(fmt.Sprintf("interfaceToTemplateValue cannot take type %T as argument. Value was %#v.", v, v))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----- BytesValue -----------------------------------------------------------------
|
// ----- BytesValue ------------------------------------------------------------
|
||||||
type BytesValue struct {
|
type BytesValue struct {
|
||||||
value []byte
|
value []byte
|
||||||
}
|
}
|
||||||
|
@ -46,12 +52,26 @@ type BytesValue struct {
|
||||||
func (b BytesValue) Bytes() []byte {
|
func (b BytesValue) Bytes() []byte {
|
||||||
return b.value
|
return b.value
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b BytesValue) String() string {
|
func (b BytesValue) String() string {
|
||||||
return string(b.value[:])
|
return string(b.value[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----- StringValue ----------------------------------------------------------------
|
// ----- IntValue --------------------------------------------------------------
|
||||||
|
type IntValue struct {
|
||||||
|
value int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i IntValue) Bytes() []byte {
|
||||||
|
return []byte(string(i.value))
|
||||||
|
}
|
||||||
|
func (i IntValue) Int() int {
|
||||||
|
return i.value
|
||||||
|
}
|
||||||
|
func (i IntValue) String() string {
|
||||||
|
return string(i.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----- StringValue -----------------------------------------------------------
|
||||||
type StringValue struct {
|
type StringValue struct {
|
||||||
value string
|
value string
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue