github.com/nats-io/nsc@v0.0.0-20221206222106-35db9400b257/cmd/editauthorization.go (about) 1 /* 2 * Copyright 2022 The NATS Authors 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16 package cmd 17 18 import ( 19 "errors" 20 "fmt" 21 22 "github.com/nats-io/jwt/v2" 23 "github.com/nats-io/nkeys" 24 "github.com/nats-io/nsc/cmd/store" 25 "github.com/spf13/cobra" 26 ) 27 28 type EditAccountCalloutParams struct { 29 AccountContextParams 30 SignerParams 31 disable bool 32 AuthUsers []string 33 AllowedAccounts []string 34 RmAuthUsers []string 35 RmAllowedAccounts []string 36 claim *jwt.AccountClaims 37 } 38 39 func createEditAuthorizationCallout() *cobra.Command { 40 params := &EditAccountCalloutParams{} 41 cmd := &cobra.Command{ 42 Use: "authcallout", 43 Short: "Edit an account authorization callout", 44 Args: cobra.MaximumNArgs(1), 45 SilenceUsage: true, 46 RunE: func(cmd *cobra.Command, args []string) error { 47 return RunAction(cmd, args, params) 48 }, 49 } 50 51 cmd.Flags().BoolVarP(¶ms.disable, "disable", "", false, "disable external authorization") 52 cmd.Flags().StringSliceVarP(¶ms.AuthUsers, "auth-user", "", nil, "adds a user public key that bypasses the authorization callout and is used by the authorization service itself") 53 cmd.Flags().StringSliceVarP(¶ms.AllowedAccounts, "allowed-account", "", nil, "adds an account public key that the authorization service can bind authorized users to") 54 55 cmd.Flags().StringSliceVarP(¶ms.RmAuthUsers, "rm-auth-user", "", nil, "removes a user public key that bypasses the authorization callout and is used by the authorization service itself") 56 cmd.Flags().StringSliceVarP(¶ms.RmAllowedAccounts, "rm-allowed-account", "", nil, "removes an account public key that the authorization service can bind authorized users to") 57 58 //cmd.Flags().StringVarP(¶ms.AccountContextParams.Name, "name", "n", "", "account to edit") 59 params.AccountContextParams.BindFlags(cmd) 60 61 return cmd 62 } 63 64 func init() { 65 editCmd.AddCommand(createEditAuthorizationCallout()) 66 } 67 68 func (p *EditAccountCalloutParams) SetDefaults(ctx ActionCtx) error { 69 p.AccountContextParams.Name = NameFlagOrArgument(p.AccountContextParams.Name, ctx) 70 if err := p.AccountContextParams.SetDefaults(ctx); err != nil { 71 return err 72 } 73 74 p.SignerParams.SetDefaults(nkeys.PrefixByteOperator, true, ctx) 75 76 if ctx.NothingToDo("auth-user", "rm-auth-user", "allowed-account", 77 "rm-allowed-account", "disable") { 78 return errors.New("please specify some options") 79 } 80 81 return nil 82 } 83 84 func (p *EditAccountCalloutParams) PreInteractive(_ ActionCtx) error { 85 return nil 86 } 87 88 func (p *EditAccountCalloutParams) Load(ctx ActionCtx) error { 89 var err error 90 91 if err = p.AccountContextParams.Validate(ctx); err != nil { 92 return err 93 } 94 95 p.claim, err = ctx.StoreCtx().Store.ReadAccountClaim(p.AccountContextParams.Name) 96 if err != nil { 97 return err 98 } 99 100 return nil 101 } 102 103 func (p *EditAccountCalloutParams) PostInteractive(_ ActionCtx) error { 104 return nil 105 } 106 107 // toPublicKey resolves the public and checks the key for proper type 108 func toPublicKey(s string, kind nkeys.PrefixByte) (string, error) { 109 kp, err := store.ResolveKey(s) 110 if err != nil { 111 return "", err 112 } 113 if err := nkeys.CompatibleKeyPair(kp, kind); err != nil { 114 return "", fmt.Errorf("%s is not a valid %s key", s, kind) 115 } 116 return kp.PublicKey() 117 } 118 119 func (p *EditAccountCalloutParams) Validate(ctx ActionCtx) error { 120 var err error 121 122 if err := p.SignerParams.ResolveWithPriority(ctx, p.claim.Issuer); err != nil { 123 return err 124 } 125 126 if p.disable { 127 // don't look for anything 128 return nil 129 } 130 131 for idx, k := range p.AuthUsers { 132 p.AuthUsers[idx], err = toPublicKey(k, nkeys.PrefixByteUser) 133 if err != nil { 134 return err 135 } 136 } 137 138 for idx, k := range p.RmAuthUsers { 139 p.RmAuthUsers[idx], err = toPublicKey(k, nkeys.PrefixByteUser) 140 if err != nil { 141 return err 142 } 143 } 144 145 for idx, k := range p.AllowedAccounts { 146 p.AllowedAccounts[idx], err = toPublicKey(k, nkeys.PrefixByteAccount) 147 if err != nil { 148 return err 149 } 150 } 151 152 for idx, k := range p.RmAllowedAccounts { 153 p.RmAllowedAccounts[idx], err = toPublicKey(k, nkeys.PrefixByteAccount) 154 if err != nil { 155 return err 156 } 157 } 158 159 return nil 160 } 161 162 func report(list jwt.StringList, toAdd []string, hasMsgT string, missingMsgT string) []string { 163 var r = make([]string, len(toAdd)) 164 for idx, v := range toAdd { 165 if list.Contains(v) { 166 r[idx] = fmt.Sprintf(hasMsgT, v) 167 } else { 168 r[idx] = fmt.Sprintf(missingMsgT, v) 169 } 170 } 171 return r 172 } 173 174 func (p *EditAccountCalloutParams) Run(ctx ActionCtx) (store.Status, error) { 175 var userReport []string 176 var userRmReport []string 177 var accountReport []string 178 var accountRmReport []string 179 if !p.disable { 180 userReport = report(p.claim.Account.Authorization.AuthUsers, 181 p.AuthUsers, 182 "skipped adding user %q - as it's already set", 183 "added user %q") 184 185 p.claim.Account.Authorization.AuthUsers.Add(p.AuthUsers...) 186 accountReport = report(p.claim.Account.Authorization.AllowedAccounts, 187 p.AllowedAccounts, 188 "skipped adding account %q - as it's already set", 189 "added account %q") 190 p.claim.Account.Authorization.AllowedAccounts.Add(p.AllowedAccounts...) 191 192 userRmReport = report(p.claim.Account.Authorization.AuthUsers, 193 p.RmAuthUsers, 194 "deleted user %q", 195 "skipping user %q - as it's not currently set") 196 p.claim.Authorization.AuthUsers.Remove(p.RmAuthUsers...) 197 198 accountRmReport = report(p.claim.Account.Authorization.AllowedAccounts, 199 p.RmAllowedAccounts, 200 "deleted account %q", 201 "skipping account %q - as it's not currently set") 202 p.claim.Account.Authorization.AllowedAccounts.Remove(p.RmAllowedAccounts...) 203 } else { 204 p.claim.Account.Authorization = jwt.ExternalAuthorization{} 205 } 206 207 // validate before we store it 208 var vr jwt.ValidationResults 209 p.claim.Validate(&vr) 210 errs := vr.Errors() 211 if len(errs) > 0 { 212 return nil, errs[0] 213 } 214 215 // encode and report 216 token, err := p.claim.Encode(p.signerKP) 217 if err != nil { 218 return nil, err 219 } 220 221 r := store.NewDetailedReport(false) 222 StoreAccountAndUpdateStatus(ctx, token, r) 223 if r.HasNoErrors() { 224 for _, v := range userReport { 225 r.AddOK(v) 226 } 227 for _, v := range userRmReport { 228 r.AddOK(v) 229 } 230 for _, v := range accountReport { 231 r.AddOK(v) 232 } 233 for _, v := range accountRmReport { 234 r.AddOK(v) 235 } 236 if p.disable { 237 r.AddOK("removed external authorization configuration") 238 } 239 } 240 return r, err 241 }