github.com/apptainer/singularity@v3.1.1+incompatible/pkg/util/capabilities/config.go (about) 1 // Copyright (c) 2018, Sylabs Inc. All rights reserved. 2 // This software is licensed under a 3-clause BSD license. Please consult the 3 // LICENSE.md file distributed with the sources of this project regarding your 4 // rights to use or distribute this software. 5 6 package capabilities 7 8 import ( 9 "encoding/json" 10 "fmt" 11 "io" 12 "io/ioutil" 13 ) 14 15 // Caplist defines a map of users/groups with associated list of capabilities 16 type Caplist map[string][]string 17 18 // Config is the in memory representation of the user/group capability 19 // authorizations as set by an admin 20 type Config struct { 21 Users Caplist `json:"users,omitempty"` 22 Groups Caplist `json:"groups,omitempty"` 23 } 24 25 // ReadFrom reads a capability configuration from an io.Reader and returns a capability 26 // config with the set of authorized user/group capabilities 27 func ReadFrom(r io.Reader) (*Config, error) { 28 c := &Config{ 29 Users: make(Caplist, 0), 30 Groups: make(Caplist, 0), 31 } 32 33 // read all data from r into b 34 b, err := ioutil.ReadAll(r) 35 if err != nil { 36 return nil, fmt.Errorf("failed to read from io.Reader: %s", err) 37 } 38 39 if len(b) > 0 { 40 // if we had data to read in io.Reader, attempt to unmarshal as JSON 41 if err := json.Unmarshal(b, c); err != nil { 42 return nil, fmt.Errorf("failed to decode JSON data from io.Reader: %s", err) 43 } 44 } else { 45 // if no data in io.Reader, populate c with empty data 46 data, err := json.Marshal(c) 47 if err != nil { 48 return nil, fmt.Errorf("failed to initialize data") 49 } 50 json.Unmarshal(data, c) 51 } 52 53 return c, nil 54 } 55 56 // WriteTo writes the capability config into the provided io.Writer. If writing to the 57 // same file as passed to ReadFrom(io.Reader), the file should be truncated should seek to 0 58 // before passing the file as the io.Writer 59 func (c *Config) WriteTo(w io.Writer) (int64, error) { 60 json, err := json.MarshalIndent(c, "", "\t") 61 if err != nil { 62 return 0, fmt.Errorf("failed to marshall capability data to json: %v", err) 63 } 64 65 n, err := w.Write(json) 66 if err != nil { 67 return int64(n), fmt.Errorf("failed to write capability json to io.Writer: %v", err) 68 } 69 70 return int64(n), err 71 } 72 73 func (c *Config) checkCaps(caps []string) error { 74 for _, c := range caps { 75 if _, ok := Map[c]; !ok { 76 return fmt.Errorf("unknown capability %s", c) 77 } 78 } 79 return nil 80 } 81 82 // AddUserCaps adds an authorized capability set to user 83 func (c *Config) AddUserCaps(user string, caps []string) error { 84 if err := c.checkCaps(caps); err != nil { 85 return err 86 } 87 for _, cap := range caps { 88 present := false 89 for _, c := range c.Users[user] { 90 if c == cap { 91 present = true 92 } 93 } 94 if !present { 95 c.Users[user] = append(c.Users[user], cap) 96 } 97 } 98 return nil 99 } 100 101 // AddGroupCaps adds an authorized capability set to group 102 func (c *Config) AddGroupCaps(group string, caps []string) error { 103 if err := c.checkCaps(caps); err != nil { 104 return err 105 } 106 for _, cap := range caps { 107 present := false 108 for _, c := range c.Groups[group] { 109 if c == cap { 110 present = true 111 } 112 } 113 if !present { 114 c.Groups[group] = append(c.Groups[group], cap) 115 } 116 } 117 return nil 118 } 119 120 // DropUserCaps drops a set of capabilities for user 121 func (c *Config) DropUserCaps(user string, caps []string) error { 122 if err := c.checkCaps(caps); err != nil { 123 return err 124 } 125 if _, ok := c.Users[user]; !ok { 126 return fmt.Errorf("user '%s' not found", user) 127 } 128 for _, cap := range caps { 129 for i := len(c.Users[user]) - 1; i >= 0; i-- { 130 if c.Users[user][i] == cap { 131 c.Users[user] = append(c.Users[user][:i], c.Users[user][i+1:]...) 132 } 133 } 134 } 135 return nil 136 } 137 138 // DropGroupCaps drops a set of capabilities for group 139 func (c *Config) DropGroupCaps(group string, caps []string) error { 140 if err := c.checkCaps(caps); err != nil { 141 return err 142 } 143 if _, ok := c.Groups[group]; !ok { 144 return fmt.Errorf("group '%s' not found", group) 145 } 146 for _, cap := range caps { 147 for i := len(c.Groups[group]) - 1; i >= 0; i-- { 148 if c.Groups[group][i] == cap { 149 c.Groups[group] = append(c.Groups[group][:i], c.Groups[group][i+1:]...) 150 } 151 } 152 } 153 return nil 154 } 155 156 // ListUserCaps returns a capability list authorized for user 157 func (c *Config) ListUserCaps(user string) []string { 158 return c.Users[user] 159 } 160 161 // ListGroupCaps returns a capability list authorized for group 162 func (c *Config) ListGroupCaps(group string) []string { 163 return c.Groups[group] 164 } 165 166 // ListAllCaps returns capability list for both authorized users and groups 167 func (c *Config) ListAllCaps() (Caplist, Caplist) { 168 return c.Users, c.Groups 169 } 170 171 // CheckUserCaps checks if provided capability list for user are whether 172 // or not authorized by returning two lists, the first one containing 173 // authorized capabilities and the second one containing unauthorized 174 // capabilities 175 func (c *Config) CheckUserCaps(user string, caps []string) (authorized []string, unauthorized []string) { 176 for _, ca := range caps { 177 present := false 178 for _, userCap := range c.ListUserCaps(user) { 179 if userCap == ca { 180 authorized = append(authorized, ca) 181 present = true 182 break 183 } 184 } 185 if !present { 186 unauthorized = append(unauthorized, ca) 187 } 188 } 189 return authorized, unauthorized 190 } 191 192 // CheckGroupCaps checks if provided capability list for group are whether 193 // or not authorized by returning two lists, the first one containing 194 // authorized capabilities and the second one containing unauthorized 195 // capabilities 196 func (c *Config) CheckGroupCaps(group string, caps []string) (authorized []string, unauthorized []string) { 197 for _, ca := range caps { 198 present := false 199 for _, groupCap := range c.ListGroupCaps(group) { 200 if groupCap == ca { 201 authorized = append(authorized, ca) 202 present = true 203 break 204 } 205 } 206 if !present { 207 unauthorized = append(unauthorized, ca) 208 } 209 } 210 return authorized, unauthorized 211 }