Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions api/client/accesslist/accesslist.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ func (c *Client) GetAccessList(ctx context.Context, name string) (*accesslist.Ac
return nil, trace.Wrap(err)
}

accessList, err := conv.FromProto(resp)
accessList, err := conv.FromProto(resp, conv.WithOwnersIneligibleStatusField(resp.GetSpec().GetOwners()))
return accessList, trace.Wrap(err)
}

Expand Down Expand Up @@ -128,9 +128,9 @@ func (c *Client) ListAccessListMembers(ctx context.Context, accessList string, p
}

members = make([]*accesslist.AccessListMember, len(resp.Members))
for i, accessList := range resp.Members {
for i, member := range resp.Members {
var err error
members[i], err = conv.FromMemberProto(accessList)
members[i], err = conv.FromMemberProto(member, conv.WithMemberIneligibleStatusField(member))
if err != nil {
return nil, "", trace.Wrap(err)
}
Expand All @@ -149,7 +149,7 @@ func (c *Client) GetAccessListMember(ctx context.Context, accessList string, mem
return nil, trace.Wrap(err)
}

member, err := conv.FromMemberProto(resp)
member, err := conv.FromMemberProto(resp, conv.WithMemberIneligibleStatusField(resp))
return member, trace.Wrap(err)
}

Expand Down
287 changes: 200 additions & 87 deletions api/gen/proto/go/teleport/accesslist/v1/accesslist.pb.go

Large diffs are not rendered by default.

25 changes: 25 additions & 0 deletions api/proto/teleport/accesslist/v1/accesslist.proto
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ message AccessListOwner {

// description is the plaintext description of the owner and why they are an owner.
string description = 2;

// ineligible_status describes if this owner is eligible or not
// and if not, describes how they're lacking eligibility.
IneligibleStatus ineligible_status = 3;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing comment

}

// AccessListAudit describes the audit configuration for an access list.
Expand Down Expand Up @@ -130,4 +134,25 @@ message MemberSpec {

// added_by is the user that added this user to the access list.
string added_by = 6;

// ineligible_status describes if this member is eligible or not
// and if not, describes how they're lacking eligibility.
IneligibleStatus ineligible_status = 7;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing comment

}

// IneligibleStatus describes how the user is ineligible.
enum IneligibleStatus {
// INELIGIBLE_STATUS_UNSPECIFIED means eligiblity is unknown.
INELIGIBLE_STATUS_UNSPECIFIED = 0;
// INELIGIBLE_STATUS_ELIGIBLE means checks were done and user met all requirements.
INELIGIBLE_STATUS_ELIGIBLE = 1;
// INELIGIBLE_STATUS_USER_NOT_EXIST means user was not found in backend.
INELIGIBLE_STATUS_USER_NOT_EXIST = 2;
// INELIGIBLE_STATUS_MISSING_REQUIREMENTS means user is missing some requirements
// defined by AccessListRequires (fields can be either ownership_requires
// or membership_requires)
INELIGIBLE_STATUS_MISSING_REQUIREMENTS = 3;
// INELIGIBLE_STATUS_EXPIRED means user is expired.
// Only applicable to members.
INELIGIBLE_STATUS_EXPIRED = 4;
}
8 changes: 8 additions & 0 deletions api/types/accesslist/accesslist.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ type Owner struct {

// Description is the plaintext description of the owner and why they are an owner.
Description string `json:"description" yaml:"description"`

// IneligibleStatus describes the reason why this owner is not eligible.
IneligibleStatus string `json:"ineligible_status" yaml:"ineligible_status"`
}

// Audit describes the audit configuration for an access list.
Expand Down Expand Up @@ -178,6 +181,11 @@ func (a *AccessList) GetOwners() []Owner {
return a.Spec.Owners
}

// GetOwners returns the list of owners from the access list.
func (a *AccessList) SetOwners(owners []Owner) {
a.Spec.Owners = owners
}

// GetAuditFrequency returns the audit frequency from the access list.
func (a *AccessList) GetAuditFrequency() time.Duration {
return a.Spec.Audit.Frequency
Expand Down
37 changes: 34 additions & 3 deletions api/types/accesslist/convert/v1/accesslist.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@ import (
traitv1 "github.com/gravitational/teleport/api/types/trait/convert/v1"
)

type AccessListOption func(*accesslist.AccessList)

// FromProto converts a v1 access list into an internal access list object.
func FromProto(msg *accesslistv1.AccessList) (*accesslist.AccessList, error) {
func FromProto(msg *accesslistv1.AccessList, opts ...AccessListOption) (*accesslist.AccessList, error) {
if msg == nil {
return nil, trace.BadParameter("access list message is nil")
}
Expand All @@ -54,6 +56,9 @@ func FromProto(msg *accesslistv1.AccessList) (*accesslist.AccessList, error) {
owners[i] = accesslist.Owner{
Name: owner.Name,
Description: owner.Description,
// Set it to empty as default.
// Must provide as options to set it with the provided value.
IneligibleStatus: "",
}
}

Expand All @@ -79,16 +84,25 @@ func FromProto(msg *accesslistv1.AccessList) (*accesslist.AccessList, error) {
},
})

for _, opt := range opts {
opt(accessList)
}

return accessList, trace.Wrap(err)
}

// ToProto converts an internal access list into a v1 access list object.
func ToProto(accessList *accesslist.AccessList) *accesslistv1.AccessList {
owners := make([]*accesslistv1.AccessListOwner, len(accessList.Spec.Owners))
for i, owner := range accessList.Spec.Owners {
var ineligibleStatus accesslistv1.IneligibleStatus
if enumVal, ok := accesslistv1.IneligibleStatus_value[owner.IneligibleStatus]; ok {
ineligibleStatus = accesslistv1.IneligibleStatus(enumVal)
}
owners[i] = &accesslistv1.AccessListOwner{
Name: owner.Name,
Description: owner.Description,
Name: owner.Name,
Description: owner.Description,
IneligibleStatus: ineligibleStatus,
}
}

Expand Down Expand Up @@ -117,3 +131,20 @@ func ToProto(accessList *accesslist.AccessList) *accesslistv1.AccessList {
},
}
}

// WithOwnersIneligibleStatusField sets the "ineligibleStatus" field to the provided proto value.
func WithOwnersIneligibleStatusField(protoOwners []*accesslistv1.AccessListOwner) AccessListOption {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: godoc.

return func(a *accesslist.AccessList) {
updatedOwners := make([]accesslist.Owner, len(a.GetOwners()))
for i, owner := range a.GetOwners() {
protoIneligibleStatus := protoOwners[i].GetIneligibleStatus()
ineligibleStatus := ""
if protoIneligibleStatus != accesslistv1.IneligibleStatus_INELIGIBLE_STATUS_UNSPECIFIED {
ineligibleStatus = protoIneligibleStatus.String()
}
owner.IneligibleStatus = ineligibleStatus
updatedOwners[i] = owner
}
a.SetOwners(updatedOwners)
}
}
48 changes: 48 additions & 0 deletions api/types/accesslist/convert/v1/accesslist_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,58 @@ import (
"github.com/google/go-cmp/cmp"
"github.com/stretchr/testify/require"

accesslistv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/accesslist/v1"
"github.com/gravitational/teleport/api/types/accesslist"
"github.com/gravitational/teleport/api/types/header"
)

func TestWithOwnersIneligibleStatusField(t *testing.T) {
proto := []*accesslistv1.AccessListOwner{
{
Name: "expired",
IneligibleStatus: accesslistv1.IneligibleStatus_INELIGIBLE_STATUS_EXPIRED,
},
{
Name: "missing",
IneligibleStatus: accesslistv1.IneligibleStatus_INELIGIBLE_STATUS_MISSING_REQUIREMENTS,
},
{
Name: "dne",
IneligibleStatus: accesslistv1.IneligibleStatus_INELIGIBLE_STATUS_USER_NOT_EXIST,
},
}

owners := []accesslist.Owner{
{Name: "expired"},
{Name: "missing"},
{Name: "dne"},
}
al := &accesslist.AccessList{
Spec: accesslist.Spec{
Owners: owners,
},
}
require.Empty(t, cmp.Diff(al.Spec.Owners, owners))

fn := WithOwnersIneligibleStatusField(proto)
fn(al)

require.Empty(t, cmp.Diff(al.Spec.Owners, []accesslist.Owner{
{
Name: "expired",
IneligibleStatus: accesslistv1.IneligibleStatus_INELIGIBLE_STATUS_EXPIRED.String(),
},
{
Name: "missing",
IneligibleStatus: accesslistv1.IneligibleStatus_INELIGIBLE_STATUS_MISSING_REQUIREMENTS.String(),
},
{
Name: "dne",
IneligibleStatus: accesslistv1.IneligibleStatus_INELIGIBLE_STATUS_USER_NOT_EXIST.String(),
},
}))
}

func TestRoundtrip(t *testing.T) {
accessList := newAccessList(t, "access-list")

Expand Down
41 changes: 34 additions & 7 deletions api/types/accesslist/convert/v1/member.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,10 @@ import (
headerv1 "github.com/gravitational/teleport/api/types/header/convert/v1"
)

type MemberOption func(*accesslist.AccessListMember)

// FromMemberProto converts a v1 access list member into an internal access list member object.
func FromMemberProto(msg *accesslistv1.Member) (*accesslist.AccessListMember, error) {
func FromMemberProto(msg *accesslistv1.Member, opts ...MemberOption) (*accesslist.AccessListMember, error) {
if msg == nil {
return nil, trace.BadParameter("access list message is nil")
}
Expand All @@ -42,8 +44,15 @@ func FromMemberProto(msg *accesslistv1.Member) (*accesslist.AccessListMember, er
Expires: msg.Spec.Expires.AsTime(),
Reason: msg.Spec.Reason,
AddedBy: msg.Spec.AddedBy,
// Set it to empty as default.
// Must provide as options to set it with the provided value.
IneligibleStatus: "",
})

for _, opt := range opts {
opt(member)
}

return member, trace.Wrap(err)
}

Expand All @@ -62,15 +71,21 @@ func FromMembersProto(msgs []*accesslistv1.Member) ([]*accesslist.AccessListMemb

// ToMemberProto converts an internal access list member into a v1 access list member object.
func ToMemberProto(member *accesslist.AccessListMember) *accesslistv1.Member {
var ineligibleStatus accesslistv1.IneligibleStatus
if enumVal, ok := accesslistv1.IneligibleStatus_value[member.Spec.IneligibleStatus]; ok {
ineligibleStatus = accesslistv1.IneligibleStatus(enumVal)
}

return &accesslistv1.Member{
Header: headerv1.ToResourceHeaderProto(member.ResourceHeader),
Spec: &accesslistv1.MemberSpec{
AccessList: member.Spec.AccessList,
Name: member.Spec.Name,
Joined: timestamppb.New(member.Spec.Joined),
Expires: timestamppb.New(member.Spec.Expires),
Reason: member.Spec.Reason,
AddedBy: member.Spec.AddedBy,
AccessList: member.Spec.AccessList,
Name: member.Spec.Name,
Joined: timestamppb.New(member.Spec.Joined),
Expires: timestamppb.New(member.Spec.Expires),
Reason: member.Spec.Reason,
AddedBy: member.Spec.AddedBy,
IneligibleStatus: ineligibleStatus,
},
}
}
Expand All @@ -83,3 +98,15 @@ func ToMembersProto(members []*accesslist.AccessListMember) []*accesslistv1.Memb
}
return out
}

// WithMemberIneligibleStatusField sets the "ineligibleStatus" field to the provided proto value.
func WithMemberIneligibleStatusField(protoMember *accesslistv1.Member) MemberOption {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: godoc.

return func(m *accesslist.AccessListMember) {
protoIneligibleStatus := protoMember.GetSpec().GetIneligibleStatus()
ineligibleStatus := ""
if protoIneligibleStatus != accesslistv1.IneligibleStatus_INELIGIBLE_STATUS_UNSPECIFIED {
ineligibleStatus = protoIneligibleStatus.String()
}
m.Spec.IneligibleStatus = ineligibleStatus
}
}
19 changes: 19 additions & 0 deletions api/types/accesslist/convert/v1/member_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"github.com/google/go-cmp/cmp"
"github.com/stretchr/testify/require"

accesslistv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/accesslist/v1"
"github.com/gravitational/teleport/api/types/accesslist"
"github.com/gravitational/teleport/api/types/header"
)
Expand All @@ -36,6 +37,24 @@ func TestMemberRoundtrip(t *testing.T) {
require.Empty(t, cmp.Diff(member, converted))
}

func TestWithMemberIneligibleStatusField(t *testing.T) {
proto := &accesslistv1.Member{
Spec: &accesslistv1.MemberSpec{
IneligibleStatus: accesslistv1.IneligibleStatus_INELIGIBLE_STATUS_EXPIRED,
},
}

alMember := &accesslist.AccessListMember{
Spec: accesslist.AccessListMemberSpec{},
}
require.Empty(t, alMember.Spec.IneligibleStatus)

fn := WithMemberIneligibleStatusField(proto)
fn(alMember)

require.Equal(t, accesslistv1.IneligibleStatus_INELIGIBLE_STATUS_EXPIRED.Enum().String(), alMember.Spec.IneligibleStatus)
}

// Make sure that we don't panic if any of the message fields are missing.
func TestMemberFromProtoNils(t *testing.T) {
// Spec is nil
Expand Down
3 changes: 3 additions & 0 deletions api/types/accesslist/member.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ type AccessListMemberSpec struct {

// added_by is the user that added this user to the access list.
AddedBy string `json:"added_by" yaml:"added_by"`

// IneligibleStatus describes the reason why this member is not eligible.
IneligibleStatus string `json:"ineligible_status" yaml:"ineligible_status"`
}

// NewAccessListMember will create a new access listm member.
Expand Down