github.com/gravitational/teleport/api@v0.0.0-20240507183017-3110591cbafc/types/access_request.go (about)

     1  /*
     2  Copyright 2020 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 contains all types and logic required by the Teleport API.
    18  package types
    19  
    20  import (
    21  	"fmt"
    22  	"reflect"
    23  	"slices"
    24  	"sort"
    25  	"time"
    26  
    27  	"github.com/gravitational/trace"
    28  
    29  	"github.com/gravitational/teleport/api/constants"
    30  	"github.com/gravitational/teleport/api/utils"
    31  )
    32  
    33  // AccessRequest is a request for temporarily granted roles
    34  type AccessRequest interface {
    35  	ResourceWithLabels
    36  	// GetUser gets the name of the requesting user
    37  	GetUser() string
    38  	// GetRoles gets the roles being requested by the user
    39  	GetRoles() []string
    40  	// SetRoles overrides the roles being requested by the user
    41  	SetRoles([]string)
    42  	// GetState gets the current state of the request
    43  	GetState() RequestState
    44  	// SetState sets the approval state of the request
    45  	SetState(RequestState) error
    46  	// GetCreationTime gets the time at which the request was
    47  	// originally registered with the auth server.
    48  	GetCreationTime() time.Time
    49  	// SetCreationTime sets the creation time of the request.
    50  	SetCreationTime(time.Time)
    51  	// GetAccessExpiry gets the expiration time for the elevated certificate
    52  	// that will be issued if the Access Request is approved.
    53  	GetAccessExpiry() time.Time
    54  	// GetAssumeStartTime gets the time the roles can be assumed
    55  	// if the Access Request is approved.
    56  	GetAssumeStartTime() *time.Time
    57  	// SetAssumeStartTime sets the time the roles can be assumed
    58  	// if the Access Request is approved.
    59  	SetAssumeStartTime(time.Time)
    60  	// SetAccessExpiry sets the expiration time for the elevated certificate
    61  	// that will be issued if the Access Request is approved.
    62  	SetAccessExpiry(time.Time)
    63  	// GetSessionTLL gets the session TTL for generated certificates.
    64  	GetSessionTLL() time.Time
    65  	// SetSessionTLL sets the session TTL for generated certificates.
    66  	SetSessionTLL(time.Time)
    67  	// GetRequestReason gets the reason for the request's creation.
    68  	GetRequestReason() string
    69  	// SetRequestReason sets the reason for the request's creation.
    70  	SetRequestReason(string)
    71  	// GetResolveReason gets the reason for the request's resolution.
    72  	GetResolveReason() string
    73  	// SetResolveReason sets the reason for the request's resolution.
    74  	SetResolveReason(string)
    75  	// GetResolveAnnotations gets the annotations associated with
    76  	// the request's resolution.
    77  	GetResolveAnnotations() map[string][]string
    78  	// SetResolveAnnotations sets the annotations associated with
    79  	// the request's resolution.
    80  	SetResolveAnnotations(map[string][]string)
    81  	// GetSystemAnnotations gets the teleport-applied annotations.
    82  	GetSystemAnnotations() map[string][]string
    83  	// SetSystemAnnotations sets the teleport-applied annotations.
    84  	SetSystemAnnotations(map[string][]string)
    85  	// GetOriginalRoles gets the original (pre-override) role list.
    86  	GetOriginalRoles() []string
    87  	// GetThresholds gets the review thresholds.
    88  	GetThresholds() []AccessReviewThreshold
    89  	// SetThresholds sets the review thresholds (internal use only).
    90  	SetThresholds([]AccessReviewThreshold)
    91  	// GetRoleThresholdMapping gets the rtm.  See documentation of the
    92  	// AccessRequestSpecV3.RoleThresholdMapping field for details.
    93  	GetRoleThresholdMapping() map[string]ThresholdIndexSets
    94  	// SetRoleThresholdMapping sets the rtm (internal use only).  See documentation
    95  	// of the AccessRequestSpecV3.RoleThresholdMapping field for details.
    96  	SetRoleThresholdMapping(map[string]ThresholdIndexSets)
    97  	// GetReviews gets the list of currently applied access reviews.
    98  	GetReviews() []AccessReview
    99  	// SetReviews sets the list of currently applied access reviews (internal use only).
   100  	SetReviews([]AccessReview)
   101  	// GetPromotedAccessListName returns the access list name that this access request
   102  	// was promoted to.
   103  	GetPromotedAccessListName() string
   104  	// SetPromotedAccessListName sets the access list name that this access request
   105  	// was promoted to.
   106  	SetPromotedAccessListName(name string)
   107  	// GetPromotedAccessListTitle returns the access list title that this access request
   108  	// was promoted to.
   109  	GetPromotedAccessListTitle() string
   110  	// SetPromotedAccessListTitle sets the access list title that this access request
   111  	// was promoted to.
   112  	SetPromotedAccessListTitle(string)
   113  	// GetSuggestedReviewers gets the suggested reviewer list.
   114  	GetSuggestedReviewers() []string
   115  	// SetSuggestedReviewers sets the suggested reviewer list.
   116  	SetSuggestedReviewers([]string)
   117  	// GetRequestedResourceIDs gets the resource IDs to which access is being requested.
   118  	GetRequestedResourceIDs() []ResourceID
   119  	// SetRequestedResourceIDs sets the resource IDs to which access is being requested.
   120  	SetRequestedResourceIDs([]ResourceID)
   121  	// GetLoginHint gets the requested login hint.
   122  	GetLoginHint() string
   123  	// SetLoginHint sets the requested login hint.
   124  	SetLoginHint(string)
   125  	// GetMaxDuration gets the maximum time at which the access should be approved for.
   126  	GetMaxDuration() time.Time
   127  	// SetMaxDuration sets the maximum time at which the access should be approved for.
   128  	SetMaxDuration(time.Time)
   129  	// GetDryRun returns true if this request should not be created and is only
   130  	// a dry run to validate request capabilities.
   131  	GetDryRun() bool
   132  	// SetDryRun sets the dry run flag on the request.
   133  	SetDryRun(bool)
   134  	// Copy returns a copy of the access request resource.
   135  	Copy() AccessRequest
   136  }
   137  
   138  // NewAccessRequest assembles an AccessRequest resource.
   139  func NewAccessRequest(name string, user string, roles ...string) (AccessRequest, error) {
   140  	return NewAccessRequestWithResources(name, user, roles, []ResourceID{})
   141  }
   142  
   143  // NewAccessRequestWithResources assembles an AccessRequest resource with
   144  // requested resources.
   145  func NewAccessRequestWithResources(name string, user string, roles []string, resourceIDs []ResourceID) (AccessRequest, error) {
   146  	req := AccessRequestV3{
   147  		Metadata: Metadata{
   148  			Name: name,
   149  		},
   150  		Spec: AccessRequestSpecV3{
   151  			User:                 user,
   152  			Roles:                utils.CopyStrings(roles),
   153  			RequestedResourceIDs: append([]ResourceID{}, resourceIDs...),
   154  		},
   155  	}
   156  	if err := req.CheckAndSetDefaults(); err != nil {
   157  		return nil, trace.Wrap(err)
   158  	}
   159  	return &req, nil
   160  }
   161  
   162  // GetUser gets User
   163  func (r *AccessRequestV3) GetUser() string {
   164  	return r.Spec.User
   165  }
   166  
   167  // GetRoles gets Roles
   168  func (r *AccessRequestV3) GetRoles() []string {
   169  	return r.Spec.Roles
   170  }
   171  
   172  // SetRoles sets Roles
   173  func (r *AccessRequestV3) SetRoles(roles []string) {
   174  	r.Spec.Roles = roles
   175  }
   176  
   177  // GetState gets State
   178  func (r *AccessRequestV3) GetState() RequestState {
   179  	return r.Spec.State
   180  }
   181  
   182  // SetState sets State
   183  func (r *AccessRequestV3) SetState(state RequestState) error {
   184  	if r.Spec.State.IsDenied() {
   185  		if state.IsDenied() {
   186  			return nil
   187  		}
   188  		return trace.BadParameter("cannot set request-state %q (already denied)", state.String())
   189  	}
   190  	r.Spec.State = state
   191  	return nil
   192  }
   193  
   194  // GetCreationTime gets CreationTime
   195  func (r *AccessRequestV3) GetCreationTime() time.Time {
   196  	return r.Spec.Created
   197  }
   198  
   199  // SetCreationTime sets CreationTime
   200  func (r *AccessRequestV3) SetCreationTime(t time.Time) {
   201  	r.Spec.Created = t.UTC()
   202  }
   203  
   204  // GetAccessExpiry gets AccessExpiry
   205  func (r *AccessRequestV3) GetAccessExpiry() time.Time {
   206  	return r.Spec.Expires
   207  }
   208  
   209  // GetAssumeStartTime gets AssumeStartTime
   210  func (r *AccessRequestV3) GetAssumeStartTime() *time.Time {
   211  	return r.Spec.AssumeStartTime
   212  }
   213  
   214  // SetAssumeStartTime sets AssumeStartTime
   215  func (r *AccessRequestV3) SetAssumeStartTime(t time.Time) {
   216  	r.Spec.AssumeStartTime = &t
   217  }
   218  
   219  // SetAccessExpiry sets AccessExpiry
   220  func (r *AccessRequestV3) SetAccessExpiry(expiry time.Time) {
   221  	r.Spec.Expires = expiry.UTC()
   222  }
   223  
   224  // GetSessionTLL gets SessionTLL
   225  func (r *AccessRequestV3) GetSessionTLL() time.Time {
   226  	return r.Spec.SessionTTL
   227  }
   228  
   229  // SetSessionTLL sets SessionTLL
   230  func (r *AccessRequestV3) SetSessionTLL(t time.Time) {
   231  	r.Spec.SessionTTL = t.UTC()
   232  }
   233  
   234  // GetRequestReason gets RequestReason
   235  func (r *AccessRequestV3) GetRequestReason() string {
   236  	return r.Spec.RequestReason
   237  }
   238  
   239  // SetRequestReason sets RequestReason
   240  func (r *AccessRequestV3) SetRequestReason(reason string) {
   241  	r.Spec.RequestReason = reason
   242  }
   243  
   244  // GetResolveReason gets ResolveReason
   245  func (r *AccessRequestV3) GetResolveReason() string {
   246  	return r.Spec.ResolveReason
   247  }
   248  
   249  // SetResolveReason sets ResolveReason
   250  func (r *AccessRequestV3) SetResolveReason(reason string) {
   251  	r.Spec.ResolveReason = reason
   252  }
   253  
   254  // GetResolveAnnotations gets ResolveAnnotations
   255  func (r *AccessRequestV3) GetResolveAnnotations() map[string][]string {
   256  	return r.Spec.ResolveAnnotations
   257  }
   258  
   259  // SetResolveAnnotations sets ResolveAnnotations
   260  func (r *AccessRequestV3) SetResolveAnnotations(annotations map[string][]string) {
   261  	r.Spec.ResolveAnnotations = annotations
   262  }
   263  
   264  // GetSystemAnnotations gets SystemAnnotations
   265  func (r *AccessRequestV3) GetSystemAnnotations() map[string][]string {
   266  	return r.Spec.SystemAnnotations
   267  }
   268  
   269  // SetSystemAnnotations sets SystemAnnotations
   270  func (r *AccessRequestV3) SetSystemAnnotations(annotations map[string][]string) {
   271  	r.Spec.SystemAnnotations = annotations
   272  }
   273  
   274  func (r *AccessRequestV3) GetOriginalRoles() []string {
   275  	if l := len(r.Spec.RoleThresholdMapping); l == 0 || l == len(r.Spec.Roles) {
   276  		// rtm is unspecified or original role list is unmodified.  since the rtm
   277  		// keys and role list are identical until role subselection is applied,
   278  		// we can return the role list directly.
   279  		return r.Spec.Roles
   280  	}
   281  
   282  	// role subselection has been applied.  calculate original roles
   283  	// by collecting the keys of the rtm.
   284  	roles := make([]string, 0, len(r.Spec.RoleThresholdMapping))
   285  	for role := range r.Spec.RoleThresholdMapping {
   286  		roles = append(roles, role)
   287  	}
   288  	sort.Strings(roles)
   289  	return roles
   290  }
   291  
   292  // GetThresholds gets the review thresholds.
   293  func (r *AccessRequestV3) GetThresholds() []AccessReviewThreshold {
   294  	return r.Spec.Thresholds
   295  }
   296  
   297  // SetThresholds sets the review thresholds.
   298  func (r *AccessRequestV3) SetThresholds(thresholds []AccessReviewThreshold) {
   299  	r.Spec.Thresholds = thresholds
   300  }
   301  
   302  // GetRoleThresholdMapping gets the rtm.
   303  func (r *AccessRequestV3) GetRoleThresholdMapping() map[string]ThresholdIndexSets {
   304  	return r.Spec.RoleThresholdMapping
   305  }
   306  
   307  // SetRoleThresholdMapping sets the rtm (internal use only).
   308  func (r *AccessRequestV3) SetRoleThresholdMapping(rtm map[string]ThresholdIndexSets) {
   309  	r.Spec.RoleThresholdMapping = rtm
   310  }
   311  
   312  // SetReviews sets the list of currently applied access reviews.
   313  func (r *AccessRequestV3) SetReviews(revs []AccessReview) {
   314  	utcRevs := make([]AccessReview, len(revs))
   315  	for i, rev := range revs {
   316  		utcRevs[i] = rev
   317  		utcRevs[i].Created = rev.Created.UTC()
   318  	}
   319  	r.Spec.Reviews = utcRevs
   320  }
   321  
   322  // GetReviews gets the list of currently applied access reviews.
   323  func (r *AccessRequestV3) GetReviews() []AccessReview {
   324  	return r.Spec.Reviews
   325  }
   326  
   327  // GetSuggestedReviewers gets the suggested reviewer list.
   328  func (r *AccessRequestV3) GetSuggestedReviewers() []string {
   329  	return r.Spec.SuggestedReviewers
   330  }
   331  
   332  // SetSuggestedReviewers sets the suggested reviewer list.
   333  func (r *AccessRequestV3) SetSuggestedReviewers(reviewers []string) {
   334  	r.Spec.SuggestedReviewers = reviewers
   335  }
   336  
   337  // GetPromotedAccessListName returns PromotedAccessListName.
   338  func (r *AccessRequestV3) GetPromotedAccessListName() string {
   339  	if r.Spec.AccessList == nil {
   340  		return ""
   341  	}
   342  	return r.Spec.AccessList.Name
   343  }
   344  
   345  // SetPromotedAccessListName sets PromotedAccessListName.
   346  func (r *AccessRequestV3) SetPromotedAccessListName(name string) {
   347  	if r.Spec.AccessList == nil {
   348  		r.Spec.AccessList = &PromotedAccessList{}
   349  	}
   350  	r.Spec.AccessList.Name = name
   351  }
   352  
   353  // GetPromotedAccessListTitle returns PromotedAccessListTitle.
   354  func (r *AccessRequestV3) GetPromotedAccessListTitle() string {
   355  	if r.Spec.AccessList == nil {
   356  		return ""
   357  	}
   358  	return r.Spec.AccessList.Title
   359  }
   360  
   361  // SetPromotedAccessListTitle sets PromotedAccessListTitle.
   362  func (r *AccessRequestV3) SetPromotedAccessListTitle(title string) {
   363  	if r.Spec.AccessList == nil {
   364  		r.Spec.AccessList = &PromotedAccessList{}
   365  	}
   366  	r.Spec.AccessList.Title = title
   367  }
   368  
   369  // setStaticFields sets static resource header and metadata fields.
   370  func (r *AccessRequestV3) setStaticFields() {
   371  	r.Kind = KindAccessRequest
   372  	r.Version = V3
   373  }
   374  
   375  // CheckAndSetDefaults validates set values and sets default values
   376  func (r *AccessRequestV3) CheckAndSetDefaults() error {
   377  	r.setStaticFields()
   378  	if err := r.Metadata.CheckAndSetDefaults(); err != nil {
   379  		return trace.Wrap(err)
   380  	}
   381  
   382  	if r.Spec.State.IsNone() {
   383  		r.Spec.State = RequestState_PENDING
   384  	}
   385  
   386  	if r.GetState().IsPending() {
   387  		if r.GetResolveReason() != "" {
   388  			return trace.BadParameter("pending requests cannot include resolve reason")
   389  		}
   390  		if len(r.GetResolveAnnotations()) != 0 {
   391  			return trace.BadParameter("pending requests cannot include resolve annotations")
   392  		}
   393  	}
   394  
   395  	if r.GetUser() == "" {
   396  		return trace.BadParameter("access request user name not set")
   397  	}
   398  
   399  	if r.Spec.Roles == nil {
   400  		r.Spec.Roles = []string{}
   401  	}
   402  	if r.Spec.RequestedResourceIDs == nil {
   403  		r.Spec.RequestedResourceIDs = []ResourceID{}
   404  	}
   405  	if len(r.GetRoles()) == 0 && len(r.GetRequestedResourceIDs()) == 0 {
   406  		return trace.BadParameter("access request does not specify any roles or resources")
   407  	}
   408  
   409  	// dedupe and sort roles to simplify comparing role lists
   410  	r.Spec.Roles = utils.Deduplicate(r.Spec.Roles)
   411  	sort.Strings(r.Spec.Roles)
   412  
   413  	return nil
   414  }
   415  
   416  // GetKind gets Kind
   417  func (r *AccessRequestV3) GetKind() string {
   418  	return r.Kind
   419  }
   420  
   421  // GetSubKind gets SubKind
   422  func (r *AccessRequestV3) GetSubKind() string {
   423  	return r.SubKind
   424  }
   425  
   426  // SetSubKind sets SubKind
   427  func (r *AccessRequestV3) SetSubKind(subKind string) {
   428  	r.SubKind = subKind
   429  }
   430  
   431  // GetVersion gets Version
   432  func (r *AccessRequestV3) GetVersion() string {
   433  	return r.Version
   434  }
   435  
   436  // GetName gets Name
   437  func (r *AccessRequestV3) GetName() string {
   438  	return r.Metadata.Name
   439  }
   440  
   441  // SetName sets Name
   442  func (r *AccessRequestV3) SetName(name string) {
   443  	r.Metadata.Name = name
   444  }
   445  
   446  // Expiry gets Expiry
   447  func (r *AccessRequestV3) Expiry() time.Time {
   448  	return r.Metadata.Expiry()
   449  }
   450  
   451  // SetExpiry sets Expiry
   452  func (r *AccessRequestV3) SetExpiry(expiry time.Time) {
   453  	r.Metadata.SetExpiry(expiry.UTC())
   454  }
   455  
   456  // GetMetadata gets Metadata
   457  func (r *AccessRequestV3) GetMetadata() Metadata {
   458  	return r.Metadata
   459  }
   460  
   461  // GetResourceID gets ResourceID
   462  func (r *AccessRequestV3) GetResourceID() int64 {
   463  	return r.Metadata.GetID()
   464  }
   465  
   466  // SetResourceID sets ResourceID
   467  func (r *AccessRequestV3) SetResourceID(id int64) {
   468  	r.Metadata.SetID(id)
   469  }
   470  
   471  // GetRevision returns the revision
   472  func (r *AccessRequestV3) GetRevision() string {
   473  	return r.Metadata.GetRevision()
   474  }
   475  
   476  // SetRevision sets the revision
   477  func (r *AccessRequestV3) SetRevision(rev string) {
   478  	r.Metadata.SetRevision(rev)
   479  }
   480  
   481  // GetRequestedResourceIDs gets the resource IDs to which access is being requested.
   482  func (r *AccessRequestV3) GetRequestedResourceIDs() []ResourceID {
   483  	return append([]ResourceID{}, r.Spec.RequestedResourceIDs...)
   484  }
   485  
   486  // SetRequestedResourceIDs sets the resource IDs to which access is being requested.
   487  func (r *AccessRequestV3) SetRequestedResourceIDs(ids []ResourceID) {
   488  	r.Spec.RequestedResourceIDs = append([]ResourceID{}, ids...)
   489  }
   490  
   491  // GetLoginHint gets the requested login hint.
   492  func (r *AccessRequestV3) GetLoginHint() string {
   493  	return r.Spec.LoginHint
   494  }
   495  
   496  // SetLoginHint sets the requested login hint.
   497  func (r *AccessRequestV3) SetLoginHint(login string) {
   498  	r.Spec.LoginHint = login
   499  }
   500  
   501  // GetDryRun returns true if this request should not be created and is only
   502  // a dry run to validate request capabilities.
   503  func (r *AccessRequestV3) GetDryRun() bool {
   504  	return r.Spec.DryRun
   505  }
   506  
   507  // GetMaxDuration gets the maximum time at which the access should be approved for.
   508  func (r *AccessRequestV3) GetMaxDuration() time.Time {
   509  	return r.Spec.MaxDuration
   510  }
   511  
   512  // SetMaxDuration sets the maximum time at which the access should be approved for.
   513  func (r *AccessRequestV3) SetMaxDuration(t time.Time) {
   514  	r.Spec.MaxDuration = t
   515  }
   516  
   517  // SetDryRun sets the dry run flag on the request.
   518  func (r *AccessRequestV3) SetDryRun(dryRun bool) {
   519  	r.Spec.DryRun = dryRun
   520  }
   521  
   522  // Copy returns a copy of the access request resource.
   523  func (r *AccessRequestV3) Copy() AccessRequest {
   524  	return utils.CloneProtoMsg(r)
   525  }
   526  
   527  // GetLabel retrieves the label with the provided key. If not found
   528  // value will be empty and ok will be false.
   529  func (r *AccessRequestV3) GetLabel(key string) (value string, ok bool) {
   530  	v, ok := r.Metadata.Labels[key]
   531  	return v, ok
   532  }
   533  
   534  // GetStaticLabels returns the access request static labels.
   535  func (r *AccessRequestV3) GetStaticLabels() map[string]string {
   536  	return r.Metadata.Labels
   537  }
   538  
   539  // SetStaticLabels sets the access request static labels.
   540  func (r *AccessRequestV3) SetStaticLabels(sl map[string]string) {
   541  	r.Metadata.Labels = sl
   542  }
   543  
   544  // GetAllLabels returns the access request static labels.
   545  func (r *AccessRequestV3) GetAllLabels() map[string]string {
   546  	return r.Metadata.Labels
   547  }
   548  
   549  // MatchSearch goes through select field values and tries to
   550  // match against the list of search values.
   551  func (r *AccessRequestV3) MatchSearch(values []string) bool {
   552  	fieldVals := append(utils.MapToStrings(r.GetAllLabels()), r.GetName(), r.GetUser())
   553  	fieldVals = append(fieldVals, r.GetRoles()...)
   554  	for _, resource := range r.GetRequestedResourceIDs() {
   555  		fieldVals = append(fieldVals, resource.Name)
   556  	}
   557  	return MatchSearch(fieldVals, values, nil)
   558  }
   559  
   560  // Origin returns the origin value of the resource.
   561  func (r *AccessRequestV3) Origin() string {
   562  	return r.Metadata.Origin()
   563  }
   564  
   565  // SetOrigin sets the origin value of the resource.
   566  func (r *AccessRequestV3) SetOrigin(origin string) {
   567  	r.Metadata.SetOrigin(origin)
   568  }
   569  
   570  // String returns a text representation of this AccessRequest
   571  func (r *AccessRequestV3) String() string {
   572  	return fmt.Sprintf("AccessRequest(user=%v,roles=%+v)", r.Spec.User, r.Spec.Roles)
   573  }
   574  
   575  func (c AccessReviewConditions) IsZero() bool {
   576  	return reflect.ValueOf(c).IsZero()
   577  }
   578  
   579  func (s AccessReviewSubmission) Check() error {
   580  	if s.RequestID == "" {
   581  		return trace.BadParameter("missing request ID")
   582  	}
   583  
   584  	return s.Review.Check()
   585  }
   586  
   587  func (s AccessReview) Check() error {
   588  	if s.Author == "" {
   589  		return trace.BadParameter("missing review author")
   590  	}
   591  
   592  	return nil
   593  }
   594  
   595  // GetAccessListName returns the access list name used for the promotion.
   596  func (s AccessReview) GetAccessListName() string {
   597  	if s.AccessList == nil {
   598  		return ""
   599  	}
   600  	return s.AccessList.Name
   601  }
   602  
   603  // GetAccessListTitle returns the access list title used for the promotion.
   604  func (s AccessReview) GetAccessListTitle() string {
   605  	if s.AccessList == nil {
   606  		return ""
   607  	}
   608  	return s.AccessList.Title
   609  }
   610  
   611  // AccessRequestUpdate encompasses the parameters of a
   612  // SetAccessRequestState call.
   613  type AccessRequestUpdate struct {
   614  	// RequestID is the ID of the request to be updated.
   615  	RequestID string
   616  	// State is the state that the target request
   617  	// should resolve to.
   618  	State RequestState
   619  	// Reason is an optional description of *why* the
   620  	// the request is being resolved.
   621  	Reason string
   622  	// Annotations supplies extra data associated with
   623  	// the resolution; primarily for audit purposes.
   624  	Annotations map[string][]string
   625  	// Roles, if non-empty declares a list of roles
   626  	// that should override the role list of the request.
   627  	// This parameter is only accepted on approvals
   628  	// and must be a subset of the role list originally
   629  	// present on the request.
   630  	Roles []string
   631  	// AssumeStartTime sets the time the requestor can assume
   632  	// the requested roles.
   633  	AssumeStartTime *time.Time
   634  }
   635  
   636  // Check validates the request's fields
   637  func (u *AccessRequestUpdate) Check() error {
   638  	if u.RequestID == "" {
   639  		return trace.BadParameter("missing request id")
   640  	}
   641  	if u.State.IsNone() {
   642  		return trace.BadParameter("missing request state")
   643  	}
   644  	if len(u.Roles) > 0 && !u.State.IsApproved() {
   645  		return trace.BadParameter("cannot override roles when setting state: %s", u.State)
   646  	}
   647  	return nil
   648  }
   649  
   650  // RequestStrategy is an indicator of how access requests
   651  // should be handled for holders of a given role.
   652  type RequestStrategy string
   653  
   654  const (
   655  	// RequestStrategyOptional is the default request strategy,
   656  	// indicating that no special actions/requirements exist.
   657  	RequestStrategyOptional RequestStrategy = "optional"
   658  
   659  	// RequestStrategyReason indicates that client implementations
   660  	// should automatically generate wildcard requests on login, and
   661  	// users should be prompted for a reason.
   662  	RequestStrategyReason RequestStrategy = "reason"
   663  
   664  	// RequestStrategyAlways indicates that client implementations
   665  	// should automatically generate wildcard requests on login, but
   666  	// that reasons are not required.
   667  	RequestStrategyAlways RequestStrategy = "always"
   668  )
   669  
   670  // ShouldAutoRequest checks if the request strategy
   671  // indicates that a request should be automatically
   672  // generated on login.
   673  func (s RequestStrategy) ShouldAutoRequest() bool {
   674  	switch s {
   675  	case RequestStrategyReason, RequestStrategyAlways:
   676  		return true
   677  	default:
   678  		return false
   679  	}
   680  }
   681  
   682  // RequireReason checks if the request strategy
   683  // is one that requires users to always supply
   684  // reasons with their requests.
   685  func (s RequestStrategy) RequireReason() bool {
   686  	return s == RequestStrategyReason
   687  }
   688  
   689  // stateVariants allows iteration of the expected variants
   690  // of RequestState.
   691  var stateVariants = [5]RequestState{
   692  	RequestState_NONE,
   693  	RequestState_PENDING,
   694  	RequestState_APPROVED,
   695  	RequestState_DENIED,
   696  	RequestState_PROMOTED,
   697  }
   698  
   699  // Parse attempts to interpret a value as a string representation
   700  // of a RequestState.
   701  func (s *RequestState) Parse(val string) error {
   702  	for _, state := range stateVariants {
   703  		if state.String() == val {
   704  			*s = state
   705  			return nil
   706  		}
   707  	}
   708  	return trace.BadParameter("unknown request state: %q", val)
   709  }
   710  
   711  // IsNone request state
   712  func (s RequestState) IsNone() bool {
   713  	return s == RequestState_NONE
   714  }
   715  
   716  // IsPending request state
   717  func (s RequestState) IsPending() bool {
   718  	return s == RequestState_PENDING
   719  }
   720  
   721  // IsApproved request state
   722  func (s RequestState) IsApproved() bool {
   723  	return s == RequestState_APPROVED
   724  }
   725  
   726  // IsDenied request state
   727  func (s RequestState) IsDenied() bool {
   728  	return s == RequestState_DENIED
   729  }
   730  
   731  // IsPromoted returns true is the request in the PROMOTED state.
   732  func (s RequestState) IsPromoted() bool {
   733  	return s == RequestState_PROMOTED
   734  }
   735  
   736  // IsResolved request state
   737  func (s RequestState) IsResolved() bool {
   738  	return s.IsApproved() || s.IsDenied() || s.IsPromoted()
   739  }
   740  
   741  // key values for map encoding of request filter
   742  const (
   743  	keyID    = "id"
   744  	keyUser  = "user"
   745  	keyState = "state"
   746  )
   747  
   748  // IntoMap copies AccessRequestFilter values into a map
   749  func (f *AccessRequestFilter) IntoMap() map[string]string {
   750  	m := make(map[string]string)
   751  	if f.ID != "" {
   752  		m[keyID] = f.ID
   753  	}
   754  	if f.User != "" {
   755  		m[keyUser] = f.User
   756  	}
   757  	if !f.State.IsNone() {
   758  		m[keyState] = f.State.String()
   759  	}
   760  	return m
   761  }
   762  
   763  // FromMap copies values from a map into this AccessRequestFilter value
   764  func (f *AccessRequestFilter) FromMap(m map[string]string) error {
   765  	for key, val := range m {
   766  		switch key {
   767  		case keyID:
   768  			f.ID = val
   769  		case keyUser:
   770  			f.User = val
   771  		case keyState:
   772  			if err := f.State.Parse(val); err != nil {
   773  				return trace.Wrap(err)
   774  			}
   775  		default:
   776  			return trace.BadParameter("unknown filter key %s", key)
   777  		}
   778  	}
   779  	return nil
   780  }
   781  
   782  func hasReviewed(req AccessRequest, author string) bool {
   783  	reviews := req.GetReviews()
   784  	var reviewers []string
   785  	for _, review := range reviews {
   786  		reviewers = append(reviewers, review.Author)
   787  	}
   788  	return slices.Contains(reviewers, author)
   789  }
   790  
   791  // Match checks if a given access request matches this filter.
   792  func (f *AccessRequestFilter) Match(req AccessRequest) bool {
   793  	// only return if the request was made by the api requester
   794  	if f.Scope == AccessRequestScope_MY_REQUESTS && req.GetUser() != f.Requester {
   795  		return false
   796  	}
   797  	// a user cannot review their own requests
   798  	if f.Scope == AccessRequestScope_NEEDS_REVIEW {
   799  		if req.GetUser() == f.Requester {
   800  			return false
   801  		}
   802  		if req.GetState() != RequestState_PENDING {
   803  			return false
   804  		}
   805  		if hasReviewed(req, f.Requester) {
   806  			return false
   807  		}
   808  	}
   809  	// only match if the api requester has submit a review
   810  	if f.Scope == AccessRequestScope_REVIEWED {
   811  		// users cant review their own requests so we can early return
   812  		if req.GetUser() == f.Requester {
   813  			return false
   814  		}
   815  		if !hasReviewed(req, f.Requester) {
   816  			return false
   817  		}
   818  	}
   819  	if !req.MatchSearch(f.SearchKeywords) {
   820  		return false
   821  	}
   822  	if f.ID != "" && req.GetName() != f.ID {
   823  		return false
   824  	}
   825  	if f.User != "" && req.GetUser() != f.User {
   826  		return false
   827  	}
   828  	if !f.State.IsNone() && req.GetState() != f.State {
   829  		return false
   830  	}
   831  	return true
   832  }
   833  
   834  // AccessRequests is a list of AccessRequest resources.
   835  type AccessRequests []AccessRequest
   836  
   837  // ToMap returns these access requests as a map keyed by access request name.
   838  func (a AccessRequests) ToMap() map[string]AccessRequest {
   839  	m := make(map[string]AccessRequest)
   840  	for _, accessRequest := range a {
   841  		m[accessRequest.GetName()] = accessRequest
   842  	}
   843  	return m
   844  }
   845  
   846  // AsResources returns these access requests as resources with labels.
   847  func (a AccessRequests) AsResources() (resources ResourcesWithLabels) {
   848  	for _, accessRequest := range a {
   849  		resources = append(resources, accessRequest)
   850  	}
   851  	return resources
   852  }
   853  
   854  // Len returns the slice length.
   855  func (a AccessRequests) Len() int { return len(a) }
   856  
   857  // Less compares access requests by name.
   858  func (a AccessRequests) Less(i, j int) bool { return a[i].GetName() < a[j].GetName() }
   859  
   860  // Swap swaps two access requests.
   861  func (a AccessRequests) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
   862  
   863  // NewAccessRequestAllowedPromotions returns a new AccessRequestAllowedPromotions resource.
   864  func NewAccessRequestAllowedPromotions(promotions []*AccessRequestAllowedPromotion) *AccessRequestAllowedPromotions {
   865  	if promotions == nil {
   866  		promotions = make([]*AccessRequestAllowedPromotion, 0)
   867  	}
   868  
   869  	return &AccessRequestAllowedPromotions{
   870  		Promotions: promotions,
   871  	}
   872  }
   873  
   874  // ValidateAssumeStartTime returns error if start time is in an invalid range.
   875  func ValidateAssumeStartTime(assumeStartTime time.Time, accessExpiry time.Time, creationTime time.Time) error {
   876  	// Guard against requesting a start time before the request creation time.
   877  	if assumeStartTime.Before(creationTime) {
   878  		return trace.BadParameter("assume start time has to be after %v", creationTime.Format(time.RFC3339))
   879  	}
   880  	// Guard against requesting a start time after access expiry.
   881  	if assumeStartTime.After(accessExpiry) || assumeStartTime.Equal(accessExpiry) {
   882  		return trace.BadParameter("assume start time must be prior to access expiry time at %v",
   883  			accessExpiry.Format(time.RFC3339))
   884  	}
   885  	// Access expiry can be greater than constants.MaxAssumeStartDuration, but start time
   886  	// should be on or before constants.MaxAssumeStartDuration.
   887  	maxAssumableStartTime := creationTime.Add(constants.MaxAssumeStartDuration)
   888  	if maxAssumableStartTime.Before(accessExpiry) && assumeStartTime.After(maxAssumableStartTime) {
   889  		return trace.BadParameter("assume start time is too far in the future, latest time allowed is %v",
   890  			maxAssumableStartTime.Format(time.RFC3339))
   891  	}
   892  
   893  	return nil
   894  }