diff --git a/Gopkg.lock b/Gopkg.lock index 4ce0c74..685e39e 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -2,7 +2,8 @@ [[projects]] - digest = "1:8f6002b9f5b58e8b0a908ff932100fe232f8259f782400556bdd997c4d53bca1" + branch = "master" + digest = "1:8f5ac811829608c7b2f41a0556ec269660e0490a7e8a2e1e6d30e873f61f5cea" name = "github.com/cloudtrust/common-service" packages = [ ".", @@ -10,8 +11,7 @@ "log", ] pruneopts = "" - revision = "6f43146697b189b011f98619ca2674c29d769ea0" - version = "v2.0.1" + revision = "f4ba60b596a01823f15277487cc046ca0faab79e" [[projects]] digest = "1:bb7f91ab4d1c44a3bb2651c613463c134165bda0282fca891a63b88d1b501997" diff --git a/Gopkg.toml b/Gopkg.toml index ad7d639..c6e8c4f 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -22,7 +22,7 @@ [[constraint]] name = "github.com/cloudtrust/common-service" - version = "v2.0.1" + branch = "master" [[constraint]] name = "github.com/pkg/errors" diff --git a/definitions.go b/definitions.go index 9867743..4d8f8df 100644 --- a/definitions.go +++ b/definitions.go @@ -675,10 +675,16 @@ type UserFederationProviderRepresentation struct { ProviderName *string `json:"providerName,omitempty"` } +// AttributeKey type +type AttributeKey string + +// Attributes type +type Attributes map[AttributeKey][]string + // UserRepresentation struct type UserRepresentation struct { Access *map[string]bool `json:"access,omitempty"` - Attributes *map[string][]string `json:"attributes,omitempty"` + Attributes *Attributes `json:"attributes,omitempty"` ClientConsents *[]UserConsentRepresentation `json:"clientConsents,omitempty"` ClientRoles *map[string][]string `json:"clientRoles,omitempty"` CreatedTimestamp *int64 `json:"createdTimestamp,omitempty"` diff --git a/model_toolbox.go b/model_toolbox.go new file mode 100644 index 0000000..861e5f4 --- /dev/null +++ b/model_toolbox.go @@ -0,0 +1,242 @@ +package keycloak + +import ( + "strconv" + "time" +) + +// Get a given attribute +func (a Attributes) Get(key AttributeKey) []string { + return a[key] +} + +// Set a given attribute +func (a Attributes) Set(key AttributeKey, value []string) { + a[key] = value +} + +// GetString gets the first value of a given attribute +func (a Attributes) GetString(key AttributeKey) *string { + var attrbs = a[key] + if len(attrbs) > 0 { + return &attrbs[0] + } + return nil +} + +// SetString sets the value of a given attribute +func (a Attributes) SetString(key AttributeKey, value string) { + a.Set(key, []string{value}) +} + +// GetInt gets the first value of a given attribute +func (a Attributes) GetInt(key AttributeKey) (*int, error) { + var attrbs = a[key] + if len(attrbs) > 0 { + var res64, err = strconv.ParseInt(attrbs[0], 0, 0) + var res = int(res64) + return &res, err + } + return nil, nil +} + +// SetInt sets the value of a given attribute +func (a Attributes) SetInt(key AttributeKey, value int) { + a.Set(key, []string{strconv.FormatInt(int64(value), 10)}) +} + +// GetBool gets the first value of a given attribute +func (a Attributes) GetBool(key AttributeKey) (*bool, error) { + var attrbs = a[key] + if len(attrbs) > 0 { + var res, err = strconv.ParseBool(attrbs[0]) + return &res, err + } + return nil, nil +} + +// SetBool sets the value of a given attribute +func (a Attributes) SetBool(key AttributeKey, value bool) { + a.Set(key, []string{strconv.FormatBool(value)}) +} + +// GetDate returns an attribute which contains a date value +func (a Attributes) GetDate(key AttributeKey, dateLayouts []string) *string { + var attrb = a.GetString(key) + var formatted = a.reformatDate(attrb, dateLayouts) + if formatted != nil { + a[key] = []string{*formatted} + return formatted + } + return attrb +} + +// SetDate sets a date +func (a Attributes) SetDate(key AttributeKey, value string, dateLayouts []string) { + var formatted = a.reformatDate(&value, dateLayouts) + if formatted != nil { + value = *formatted + } + a.Set(key, []string{value}) +} + +// GetTime returns an attribute which contains a date value +func (a Attributes) GetTime(key AttributeKey, dateLayouts []string) (*time.Time, error) { + return a.parseDate(a.GetString(key), dateLayouts) +} + +// SetTime sets a date +func (a Attributes) SetTime(key AttributeKey, value time.Time, dateLayout string) { + a.Set(key, []string{value.Format(dateLayout)}) +} + +// SetStringWhenNotNil sets an attribute value if it is not nil +func (a Attributes) SetStringWhenNotNil(key AttributeKey, value *string) { + if value != nil { + a.Set(key, []string{*value}) + } +} + +// SetIntWhenNotNil sets an attribute value if it is not nil +func (a Attributes) SetIntWhenNotNil(key AttributeKey, value *int) { + if value != nil { + a.Set(key, []string{strconv.FormatInt(int64(*value), 10)}) + } +} + +// SetBoolWhenNotNil sets an attribute value if it is not nil +func (a Attributes) SetBoolWhenNotNil(key AttributeKey, value *bool) { + if value != nil { + a.Set(key, []string{strconv.FormatBool(*value)}) + } +} + +// SetDateWhenNotNil sets a date attribute if it is not nil +func (a Attributes) SetDateWhenNotNil(key AttributeKey, value *string, dateLayouts []string) { + if value != nil { + a.SetDate(key, *value, dateLayouts) + } +} + +// SetTimeWhenNotNil sets a date attribute if it is not nil +func (a Attributes) SetTimeWhenNotNil(key AttributeKey, value *time.Time, dateLayout string) { + if value != nil { + a.SetTime(key, *value, dateLayout) + } +} + +func (a Attributes) parseDate(value *string, dateLayouts []string) (*time.Time, error) { + if value == nil || len(dateLayouts) == 0 { + return nil, nil + } + var date, firstErr = time.Parse(dateLayouts[0], *value) + if firstErr == nil { + return &date, nil + } + + // Date does not have the expected layout. Try to convert it from supported layouts + var err error + for _, layout := range dateLayouts[1:] { + date, err = time.Parse(layout, *value) + if err == nil { + return &date, nil + } + } + + return nil, firstErr +} + +func (a Attributes) reformatDate(value *string, dateLayouts []string) *string { + var date, err = a.parseDate(value, dateLayouts) + if err != nil || date == nil { + return nil + } + var res = date.Format(dateLayouts[0]) + return &res +} + +// GetAttribute returns an attribute given its key +func (u *UserRepresentation) GetAttribute(key AttributeKey) []string { + if u.Attributes != nil { + return u.Attributes.Get(key) + } + return nil +} + +// SetAttribute sets an attribute +func (u *UserRepresentation) SetAttribute(key AttributeKey, value []string) { + if u.Attributes == nil { + var attrbs = make(Attributes) + u.Attributes = &attrbs + } + u.Attributes.Set(key, value) +} + +// GetAttributeString returns the first value of an attribute given its key +func (u *UserRepresentation) GetAttributeString(key AttributeKey) *string { + if u.Attributes != nil { + return u.Attributes.GetString(key) + } + return nil +} + +// SetAttributeString sets an attribute with a single value +func (u *UserRepresentation) SetAttributeString(key AttributeKey, value string) { + u.SetAttribute(key, []string{value}) +} + +// GetAttributeBool returns the first value of an attribute given its key +func (u *UserRepresentation) GetAttributeBool(key AttributeKey) (*bool, error) { + if u.Attributes != nil { + return u.Attributes.GetBool(key) + } + return nil, nil +} + +// SetAttributeBool sets an attribute with a single value +func (u *UserRepresentation) SetAttributeBool(key AttributeKey, value bool) { + u.SetAttribute(key, []string{strconv.FormatBool(value)}) +} + +// GetAttributeInt returns the first value of an attribute given its key +func (u *UserRepresentation) GetAttributeInt(key AttributeKey) (*int, error) { + if u.Attributes != nil { + return u.Attributes.GetInt(key) + } + return nil, nil +} + +// SetAttributeInt sets an attribute with a single value +func (u *UserRepresentation) SetAttributeInt(key AttributeKey, value int) { + u.SetAttribute(key, []string{strconv.FormatInt(int64(value), 10)}) +} + +// GetAttributeDate returns an attribute which contains a date value +func (u *UserRepresentation) GetAttributeDate(key AttributeKey, dateLayouts []string) *string { + if u.Attributes != nil { + return u.Attributes.GetDate(key, dateLayouts) + } + return nil +} + +// SetAttributeDate sets a date attribute +func (u *UserRepresentation) SetAttributeDate(key AttributeKey, date string, dateLayouts []string) { + if u.Attributes == nil { + var attrbs = make(Attributes) + u.Attributes = &attrbs + } + u.Attributes.SetDate(key, date, dateLayouts) +} + +// GetAttributeTime returns an attribute which contains a date value +func (u *UserRepresentation) GetAttributeTime(key AttributeKey, dateLayouts []string) (*time.Time, error) { + if u.Attributes != nil { + return u.Attributes.GetTime(key, dateLayouts) + } + return nil, nil +} + +// SetAttributeTime sets a date attribute +func (u *UserRepresentation) SetAttributeTime(key AttributeKey, date time.Time, dateLayout string) { + u.SetAttributeString(key, date.Format(dateLayout)) +} diff --git a/model_toolbox_test.go b/model_toolbox_test.go new file mode 100644 index 0000000..fb5dadf --- /dev/null +++ b/model_toolbox_test.go @@ -0,0 +1,104 @@ +package keycloak + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +var ( + supportedDateLayouts = []string{"02.01.2006", "2006/01/02"} +) + +func TestUserRepresentationAttributes(t *testing.T) { + var userRep UserRepresentation + + var ( + keyMissing = AttributeKey("missing") + keyDate = AttributeKey("date") + keyGender = AttributeKey("gender") + keyMultiple = AttributeKey("multiple") + ) + + // Attributes are empty + assert.Nil(t, userRep.GetAttribute(keyMissing), "Search with no attribute") + assert.Nil(t, userRep.GetAttributeString(keyMissing), "Search with no attribute") + assert.Nil(t, userRep.GetAttributeDate(keyDate, supportedDateLayouts), "Search with no attribute") + + // Sets some attributes + userRep.SetAttributeString(keyDate, "2021/12/31") + userRep.SetAttributeString(keyGender, "M") + + // Search for a missing attribute + assert.Nil(t, userRep.GetAttribute(keyMissing), "Missing attribute") + + t.Run("Check that a Set/Get cycle gives the correct values", func(t *testing.T) { + userRep.SetAttribute(AttributeKey("multiple"), []string{"3", "7", "21"}) + assert.Equal(t, []string{"3", "7", "21"}, userRep.GetAttribute(keyMultiple), "Gets a multiple-value attribute") + assert.Equal(t, []string{"2021/12/31"}, userRep.GetAttribute(keyDate), "Gets an array") + assert.Equal(t, "2021/12/31", *userRep.GetAttributeString(keyDate), "Gets a single attribute") + assert.Equal(t, "31.12.2021", *userRep.GetAttributeDate(keyDate, supportedDateLayouts), "Gets a birthdate") + }) + + t.Run("Attributes and dates", func(t *testing.T) { + // Set a date in a different format than the one which will be used to store the information + userRep.SetAttributeDate(keyDate, "2021/12/31", supportedDateLayouts) + assert.Equal(t, "31.12.2021", *userRep.GetAttributeString(keyDate), "Gets a single attribute") + assert.Equal(t, "31.12.2021", *userRep.GetAttributeDate(keyDate, supportedDateLayouts), "Gets a birthdate") + + // Do not override a parameter with a nil value + userRep.Attributes.SetDateWhenNotNil(keyDate, nil, supportedDateLayouts) + assert.Equal(t, "31.12.2021", *userRep.GetAttributeString(keyDate)) + var otherDate = "30.11.2022" + userRep.Attributes.SetDateWhenNotNil(keyDate, &otherDate, supportedDateLayouts) + assert.Equal(t, otherDate, *userRep.GetAttributeString(keyDate)) + }) + + t.Run("Int tests", func(t *testing.T) { + var keyInt = AttributeKey("numeric") + var res, err = userRep.Attributes.GetInt(keyInt) + var value = 5 + assert.Nil(t, err) + assert.Nil(t, res) + // Set int value + userRep.Attributes.SetIntWhenNotNil(keyInt, &value) + res, err = userRep.Attributes.GetInt(keyInt) + assert.Nil(t, err) + assert.Equal(t, value, *res) + // Set when not nil : won't have any effect with nil + userRep.Attributes.SetIntWhenNotNil(keyInt, nil) + res, err = userRep.Attributes.GetInt(keyInt) + assert.Nil(t, err) + assert.Equal(t, value, *res) + // Update to 10 + value = 10 + userRep.Attributes.SetIntWhenNotNil(keyInt, &value) + res, err = userRep.Attributes.GetInt(keyInt) + assert.Nil(t, err) + assert.Equal(t, value, *res) + }) + + t.Run("Boolean tests", func(t *testing.T) { + var keyBool = AttributeKey("boolean") + var res, err = userRep.Attributes.GetBool(keyBool) + var value = false + assert.Nil(t, err) + assert.Nil(t, res) + // Set boolean value + userRep.Attributes.SetBoolWhenNotNil(keyBool, &value) + res, err = userRep.Attributes.GetBool(keyBool) + assert.Nil(t, err) + assert.False(t, *res) + // Set when not nil : won't have any effect with nil + userRep.Attributes.SetBoolWhenNotNil(keyBool, nil) + res, err = userRep.Attributes.GetBool(keyBool) + assert.Nil(t, err) + assert.False(t, *res) + // Update to true + value = true + userRep.Attributes.SetBoolWhenNotNil(keyBool, &value) + res, err = userRep.Attributes.GetBool(keyBool) + assert.Nil(t, err) + assert.True(t, *res) + }) +}