github.com/gravitational/teleport/api@v0.0.0-20240507183017-3110591cbafc/types/headlessauthn.go (about) 1 /* 2 Copyright 2023 Gravitational, Inc. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package types 18 19 import ( 20 "time" 21 22 "github.com/gravitational/trace" 23 ) 24 25 // NewHeadlessAuthentication creates a new a headless authentication resource. 26 func NewHeadlessAuthentication(username, name string, expires time.Time) (*HeadlessAuthentication, error) { 27 ha := &HeadlessAuthentication{ 28 ResourceHeader: ResourceHeader{ 29 Metadata: Metadata{ 30 Name: name, 31 Expires: &expires, 32 }, 33 }, 34 User: username, 35 } 36 return ha, ha.CheckAndSetDefaults() 37 } 38 39 // CheckAndSetDefaults does basic validation and default setting. 40 func (h *HeadlessAuthentication) CheckAndSetDefaults() error { 41 h.setStaticFields() 42 if err := h.Metadata.CheckAndSetDefaults(); err != nil { 43 return trace.Wrap(err) 44 } 45 46 if h.Metadata.Expires == nil || h.Metadata.Expires.IsZero() { 47 return trace.BadParameter("headless authentication resource must have non-zero header.metadata.expires") 48 } 49 50 if h.User == "" { 51 return trace.BadParameter("headless authentication resource must have non-empty user") 52 } 53 54 if h.Version == "" { 55 h.Version = V1 56 } 57 58 return nil 59 } 60 61 // setStaticFields sets static resource header and metadata fields. 62 func (h *HeadlessAuthentication) setStaticFields() { 63 h.Kind = KindHeadlessAuthentication 64 } 65 66 // Stringify returns the readable string for a headless authentication state. 67 func (h HeadlessAuthenticationState) Stringify() string { 68 switch h { 69 case HeadlessAuthenticationState_HEADLESS_AUTHENTICATION_STATE_PENDING: 70 return "pending" 71 case HeadlessAuthenticationState_HEADLESS_AUTHENTICATION_STATE_DENIED: 72 return "denied" 73 case HeadlessAuthenticationState_HEADLESS_AUTHENTICATION_STATE_APPROVED: 74 return "approved" 75 default: 76 return "unknown" 77 } 78 } 79 80 // IsUnspecified headless authentication state. This usually means the headless 81 // authentication resource is a headless authentication stub, with limited data. 82 func (s HeadlessAuthenticationState) IsUnspecified() bool { 83 return s == HeadlessAuthenticationState_HEADLESS_AUTHENTICATION_STATE_UNSPECIFIED 84 } 85 86 // IsPending headless authentication state. 87 func (s HeadlessAuthenticationState) IsPending() bool { 88 return s == HeadlessAuthenticationState_HEADLESS_AUTHENTICATION_STATE_PENDING 89 } 90 91 // headlessStateVariants allows iteration of the expected variants 92 // of HeadlessAuthenticationState. 93 var headlessStateVariants = [4]HeadlessAuthenticationState{ 94 HeadlessAuthenticationState_HEADLESS_AUTHENTICATION_STATE_UNSPECIFIED, 95 HeadlessAuthenticationState_HEADLESS_AUTHENTICATION_STATE_PENDING, 96 HeadlessAuthenticationState_HEADLESS_AUTHENTICATION_STATE_DENIED, 97 HeadlessAuthenticationState_HEADLESS_AUTHENTICATION_STATE_APPROVED, 98 } 99 100 // Parse attempts to interpret a value as a string representation 101 // of a HeadlessAuthenticationState. 102 func (s *HeadlessAuthenticationState) Parse(val string) error { 103 for _, state := range headlessStateVariants { 104 if state.String() == val { 105 *s = state 106 return nil 107 } 108 } 109 return trace.BadParameter("unknown request state: %q", val) 110 } 111 112 // HeadlessAuthenticationFilter encodes filter params for headless authentications. 113 type HeadlessAuthenticationFilter struct { 114 Name string 115 Username string 116 State HeadlessAuthenticationState 117 } 118 119 // key values for map encoding of headless authn filter. 120 const ( 121 headlessFilterKeyName = "name" 122 headlessFilterKeyUsername = "username" 123 headlessFilterKeyState = "state" 124 ) 125 126 // IntoMap copies HeadlessAuthenticationFilter values into a map. 127 func (f *HeadlessAuthenticationFilter) IntoMap() map[string]string { 128 m := make(map[string]string) 129 if f.Name != "" { 130 m[headlessFilterKeyName] = f.Name 131 } 132 if f.Username != "" { 133 m[headlessFilterKeyUsername] = f.Username 134 } 135 if !f.State.IsUnspecified() { 136 m[headlessFilterKeyState] = f.State.String() 137 } 138 return m 139 } 140 141 // FromMap copies values from a map into this HeadlessAuthenticationFilter value. 142 func (f *HeadlessAuthenticationFilter) FromMap(m map[string]string) error { 143 for key, val := range m { 144 switch key { 145 case headlessFilterKeyName: 146 f.Name = val 147 case headlessFilterKeyUsername: 148 f.Username = val 149 case headlessFilterKeyState: 150 if err := f.State.Parse(val); err != nil { 151 return trace.Wrap(err) 152 } 153 default: 154 return trace.BadParameter("unknown filter key %s", key) 155 } 156 } 157 return nil 158 } 159 160 // Match checks if a given headless authentication matches this filter. 161 func (f *HeadlessAuthenticationFilter) Match(req *HeadlessAuthentication) bool { 162 if f.Name != "" && req.GetName() != f.Name { 163 return false 164 } 165 if f.Username != "" && req.User != f.Username { 166 return false 167 } 168 if !f.State.IsUnspecified() && req.State != f.State { 169 return false 170 } 171 return true 172 }