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  }