github.com/machinefi/w3bstream@v1.6.5-rc9.0.20240426031326-b8c7c4876e72/pkg/modules/access_key/access_key_models.go (about) 1 package access_key 2 3 import ( 4 "bytes" 5 "encoding/base64" 6 "fmt" 7 "strconv" 8 "time" 9 10 "github.com/pkg/errors" 11 12 base "github.com/machinefi/w3bstream/pkg/depends/base/types" 13 "github.com/machinefi/w3bstream/pkg/depends/kit/sqlx/builder" 14 "github.com/machinefi/w3bstream/pkg/depends/kit/sqlx/datatypes" 15 "github.com/machinefi/w3bstream/pkg/depends/x/stringsx" 16 "github.com/machinefi/w3bstream/pkg/enums" 17 "github.com/machinefi/w3bstream/pkg/models" 18 "github.com/machinefi/w3bstream/pkg/types" 19 ) 20 21 type CreateReqBase struct { 22 // Name access token name 23 Name string `json:"name"` 24 // ExpirationDays access token valid in ExpirationDays, if 0 means token will not be expired. 25 ExpirationDays int `json:"expirationDays,omitempty"` 26 // Description access token description 27 Desc string `json:"desc,omitempty"` 28 // Privileges operator group access privileges 29 Privileges GroupAccessPrivileges `json:"privileges,omitempty"` 30 } 31 32 type CreateAccountAccessKeyReq = CreateReqBase 33 34 type CreateReq struct { 35 // IdentityID associated with a publisher, an account or other application 36 IdentityID types.SFID `json:"identityID,omitempty"` 37 // IdentityType associated type, default associated current account 38 IdentityType enums.AccessKeyIdentityType `json:"identityType,default='1'"` 39 CreateReqBase 40 } 41 42 type CreateRsp struct { 43 Name string `json:"name"` 44 IdentityType enums.AccessKeyIdentityType `json:"identityType"` 45 IdentityID types.SFID `json:"identityID"` 46 AccessKey string `json:"accessKey"` 47 Privileges []*GroupMetaWithPrivilege `json:"privileges"` 48 ExpiredAt *types.Timestamp `json:"expiredAt,omitempty"` 49 Desc string `json:"desc,omitempty"` 50 } 51 52 type UpdateReq struct { 53 ExpirationDays int `json:"expirationDays,omitempty"` 54 Desc string `json:"desc,omitempty"` 55 Privileges GroupAccessPrivileges `json:"privileges,omitempty"` 56 } 57 58 type UpdateRsp struct { 59 Name string `json:"name"` 60 IdentityType enums.AccessKeyIdentityType `json:"identityType"` 61 IdentityID types.SFID `json:"identityID"` 62 Privileges []*GroupMetaWithPrivilege `json:"privileges"` 63 ExpiredAt *types.Timestamp `json:"expiredAt,omitempty"` 64 LastUsed *types.Timestamp `json:"lastUsed,omitempty"` 65 Desc string `json:"desc,omitempty"` 66 } 67 68 func NewListDataByModel(v *models.AccessKey) *ListData { 69 ret := &ListData{ 70 Name: v.Name, 71 Desc: v.Description, 72 OperationTimes: v.OperationTimes, 73 } 74 if !v.ExpiredAt.IsZero() { 75 ret.ExpiredAt = &base.Timestamp{Time: v.ExpiredAt.Time} 76 } 77 if !v.LastUsed.IsZero() { 78 ret.LastUsed = &base.Timestamp{Time: v.LastUsed.Time} 79 } 80 for name, perm := range v.Privileges { 81 ret.Privileges = append(ret.Privileges, GroupMetaWithPrivilege{ 82 GroupMetaBase: GroupMetaBase{ 83 Name: name, 84 Desc: gOperatorGroups[name].Desc, 85 }, 86 Perm: perm, 87 }) 88 } 89 return ret 90 } 91 92 type ListData struct { 93 Name string `json:"name"` 94 ExpiredAt *types.Timestamp `json:"expiredAt,omitempty"` 95 LastUsed *types.Timestamp `json:"lastUsed,omitempty"` 96 Privileges []GroupMetaWithPrivilege `json:"privileges,omitempty"` 97 Desc string `json:"desc,omitempty"` 98 datatypes.OperationTimes 99 } 100 101 type CondArgs struct { 102 AccountID types.SFID `name:"-"` 103 Names []string `in:"query" name:"name,omitempty"` 104 ExpiredAtBegin types.Timestamp `in:"query" name:"expiredAtBegin,omitempty"` 105 ExpiredAtEnd types.Timestamp `in:"query" name:"expiredAtEnd,omitempty"` 106 IdentityIDs types.SFIDs `in:"query" name:"identityID,omitempty"` 107 IdentityTypes []enums.AccessKeyIdentityType `in:"query" name:"identityType,omitempty"` 108 } 109 110 func (r *CondArgs) Condition() builder.SqlCondition { 111 var ( 112 m = &models.AccessKey{} 113 c []builder.SqlCondition 114 ) 115 if r.AccountID != 0 { 116 c = append(c, m.ColAccountID().Eq(r.AccountID)) 117 } 118 if len(r.Names) > 0 { 119 c = append(c, m.ColName().In(r.Names)) 120 } 121 if !r.ExpiredAtEnd.IsZero() { 122 c = append(c, m.ColExpiredAt().Lte(r.ExpiredAtEnd)) 123 } 124 if !r.ExpiredAtBegin.IsZero() { 125 c = append(c, m.ColExpiredAt().Gte(r.ExpiredAtBegin)) 126 } 127 if len(r.IdentityIDs) > 0 { 128 c = append(c, m.ColIdentityID().In(r.IdentityIDs)) 129 } 130 if len(r.IdentityTypes) > 0 { 131 c = append(c, m.ColIdentityType().In(r.IdentityTypes)) 132 } 133 return builder.And(c...) 134 } 135 136 type ListReq struct { 137 CondArgs 138 datatypes.Pager 139 } 140 141 type ListRsp struct { 142 Data []*ListData `json:"data"` 143 Total int64 `json:"total"` 144 } 145 146 var ( 147 gAccessKeyVersion = 1 148 gAccessKeyRandLength = 12 149 gBase64Encoding = base64.RawURLEncoding 150 ) 151 152 func NewAccessKeyContext(version int) *AccessKeyContext { 153 return &AccessKeyContext{ 154 Version: version, 155 Rand: stringsx.GenRandomVisibleString(gAccessKeyRandLength, '_'), 156 GenTS: time.Now(), 157 } 158 } 159 160 func NewDefaultAccessKeyContext() *AccessKeyContext { 161 return NewAccessKeyContext(gAccessKeyVersion) 162 } 163 164 type AccessKeyContext struct { 165 // Version integer version fixed field 166 Version int 167 // GenTS access key generated timestamp (utc,seconds) 168 GenTS time.Time 169 // Rand random part, length: gAccessKeyRandLength 170 Rand string 171 } 172 173 func (c *AccessKeyContext) Regenerate() { 174 c.Rand = stringsx.GenRandomVisibleString(gAccessKeyRandLength, '_') 175 } 176 177 func (c AccessKeyContext) MarshalText() ([]byte, error) { 178 return []byte("w3b_" + gBase64Encoding.EncodeToString([]byte( 179 fmt.Sprintf("%d_%d_%s", c.Version, c.GenTS.UTC().Unix(), c.Rand), 180 ))), nil 181 } 182 183 func (c *AccessKeyContext) UnmarshalText(data []byte) (err error) { 184 sep := []byte{'_'} 185 186 parts := bytes.SplitN(data, sep, 2) 187 if !bytes.Equal(parts[0], []byte("w3b")) { 188 return ErrInvalidPrefixOrPartCount 189 } 190 var raw []byte 191 raw, err = gBase64Encoding.DecodeString(string(parts[1])) 192 if err != nil { 193 err = errors.Wrap(ErrBase64DecodeFailed, err.Error()) 194 return 195 } 196 197 contents := bytes.SplitN(raw, sep, 3) 198 if len(contents) != 3 { 199 return ErrInvalidContentsPartCount 200 } 201 202 c.Version, err = strconv.Atoi(string(contents[0])) 203 if err != nil { 204 return errors.Wrap(ErrParseVersionFailed, err.Error()) 205 } 206 genTS, err := strconv.ParseInt(string(contents[1]), 10, 64) 207 if err != nil { 208 return errors.Wrap(ErrParseGenTsFailed, err.Error()) 209 } 210 c.GenTS = time.Unix(genTS, 0) 211 switch c.Version { 212 case 1: 213 c.Rand = string(contents[2]) 214 return 215 default: 216 return ErrInvalidVersion 217 } 218 } 219 220 func (c *AccessKeyContext) Equal(v *AccessKeyContext) bool { 221 return c.Version == v.Version && 222 c.GenTS.UTC().Second() == v.GenTS.UTC().Second() && 223 c.Rand == v.Rand 224 } 225 226 var ( 227 ErrInvalidPrefixOrPartCount = errors.New("invalid prefix or part count") 228 ErrBase64DecodeFailed = errors.New("base64 decode failed") 229 ErrInvalidContentsPartCount = errors.New("invalid part count of contents") 230 ErrParseVersionFailed = errors.New("parse version failed") 231 ErrParseGenTsFailed = errors.New("parse generate ts failed") 232 ErrInvalidVersion = errors.New("invalid version") 233 )