github.com/machinefi/w3bstream@v1.6.5-rc9.0.20240426031326-b8c7c4876e72/pkg/modules/access_key/access_key.go (about) 1 package access_key 2 3 import ( 4 "context" 5 "time" 6 7 "github.com/pkg/errors" 8 9 base "github.com/machinefi/w3bstream/pkg/depends/base/types" 10 "github.com/machinefi/w3bstream/pkg/depends/conf/jwt" 11 "github.com/machinefi/w3bstream/pkg/depends/kit/httptransport" 12 "github.com/machinefi/w3bstream/pkg/depends/kit/logr" 13 "github.com/machinefi/w3bstream/pkg/depends/kit/sqlx" 14 "github.com/machinefi/w3bstream/pkg/depends/kit/sqlx/builder" 15 "github.com/machinefi/w3bstream/pkg/depends/kit/sqlx/datatypes" 16 "github.com/machinefi/w3bstream/pkg/enums" 17 "github.com/machinefi/w3bstream/pkg/errors/status" 18 "github.com/machinefi/w3bstream/pkg/models" 19 "github.com/machinefi/w3bstream/pkg/types" 20 ) 21 22 func init() { 23 jwt.SetBuiltInTokenFn(Validate) 24 } 25 26 func Create(ctx context.Context, r *CreateReq) (*CreateRsp, error) { 27 d := types.MustMgrDBExecutorFromContext(ctx) 28 acc := types.MustAccountFromContext(ctx) 29 30 switch r.IdentityType { 31 case enums.ACCESS_KEY_IDENTITY_TYPE__ACCOUNT, enums.ACCESS_KEY_IDENTITY_TYPE_UNKNOWN: 32 r.IdentityID = acc.AccountID 33 r.IdentityType = enums.ACCESS_KEY_IDENTITY_TYPE__ACCOUNT 34 case enums.ACCESS_KEY_IDENTITY_TYPE__PUBLISHER: 35 default: 36 return nil, status.InvalidAccessKeyIdentityType 37 } 38 39 kctx := NewDefaultAccessKeyContext() 40 41 exp := time.Time{} 42 if r.ExpirationDays > 0 { 43 exp = time.Now().UTC().Add(time.Hour * 24 * time.Duration(r.ExpirationDays)) 44 } 45 46 m := &models.AccessKey{ 47 RelAccount: models.RelAccount{AccountID: acc.AccountID}, 48 AccessKeyInfo: models.AccessKeyInfo{ 49 IdentityID: r.IdentityID, 50 IdentityType: r.IdentityType, 51 Name: r.Name, 52 Rand: kctx.Rand, 53 ExpiredAt: types.Timestamp{Time: exp}, 54 Description: r.Desc, 55 Privileges: r.Privileges.ConvToPrivilegeModel(), 56 }, 57 OperationTimesWithDeleted: datatypes.OperationTimesWithDeleted{ 58 OperationTimes: datatypes.OperationTimes{ 59 CreatedAt: base.Timestamp{Time: kctx.GenTS}, 60 }, 61 }, 62 } 63 64 err := sqlx.NewTasks(d).With( 65 func(d sqlx.DBExecutor) error { 66 for { 67 err := m.FetchByRand(d) 68 if err != nil { 69 if sqlx.DBErr(err).IsNotFound() { 70 return nil 71 } else { 72 return status.DatabaseError.StatusErr().WithDesc(err.Error()) 73 } 74 } else { 75 kctx.Regenerate() 76 m.Rand = kctx.Rand 77 } 78 } 79 }, 80 func(d sqlx.DBExecutor) error { 81 if err := m.Create(d); err != nil { 82 if sqlx.DBErr(err).IsConflict() { 83 return status.AccessKeyNameConflict 84 } 85 return status.DatabaseError.StatusErr().WithDesc(err.Error()) 86 } 87 return nil 88 }, 89 ).Do() 90 if err != nil { 91 return nil, err 92 } 93 94 key, _ := kctx.MarshalText() 95 96 rsp := &CreateRsp{ 97 Name: r.Name, 98 IdentityType: r.IdentityType, 99 IdentityID: r.IdentityID, 100 AccessKey: string(key), 101 Privileges: ConvToGroupMetaWithPrivileges(m.Privileges), 102 Desc: r.Desc, 103 } 104 if !exp.IsZero() { 105 rsp.ExpiredAt = &types.Timestamp{Time: exp} 106 } 107 return rsp, nil 108 } 109 110 func UpdateByName(ctx context.Context, name string, r *UpdateReq) (*UpdateRsp, error) { 111 d := types.MustMgrDBExecutorFromContext(ctx) 112 acc := types.MustAccountFromContext(ctx) 113 114 m := &models.AccessKey{ 115 RelAccount: models.RelAccount{AccountID: acc.AccountID}, 116 AccessKeyInfo: models.AccessKeyInfo{Name: name}, 117 } 118 119 err := sqlx.NewTasks(d).With( 120 func(d sqlx.DBExecutor) error { 121 if err := m.FetchByAccountIDAndName(d); err != nil { 122 if sqlx.DBErr(err).IsNotFound() { 123 return status.AccessKeyNotFound 124 } 125 return status.DatabaseError.StatusErr().WithDesc(err.Error()) 126 } 127 return nil 128 }, 129 func(d sqlx.DBExecutor) error { 130 if r.Desc != "" { 131 m.Description = r.Desc 132 } 133 m.ExpiredAt = base.Timestamp{} 134 if r.ExpirationDays > 0 { 135 m.ExpiredAt = base.Timestamp{ 136 Time: time.Now().UTC().Add(time.Hour * 24 * time.Duration(r.ExpirationDays)), 137 } 138 } 139 m.Privileges = r.Privileges.ConvToPrivilegeModel() 140 _, err := d.Exec( 141 builder.Update(d.T(m)).Set( 142 m.ColDescription().ValueBy(m.Description), 143 m.ColExpiredAt().ValueBy(m.ExpiredAt), 144 m.ColPrivileges().ValueBy(m.Privileges), 145 ).Where( 146 builder.And( 147 m.ColDeletedAt().Eq(0), 148 m.ColAccountID().Eq(acc.AccountID), 149 m.ColName().Eq(name), 150 ), 151 ), 152 ) 153 if err != nil { 154 return status.DatabaseError.StatusErr().WithDesc(err.Error()) 155 } 156 return nil 157 }, 158 ).Do() 159 if err != nil { 160 return nil, err 161 } 162 rsp := &UpdateRsp{ 163 Name: m.Name, 164 IdentityType: m.IdentityType, 165 IdentityID: m.IdentityID, 166 Privileges: ConvToGroupMetaWithPrivileges(m.Privileges), 167 Desc: m.Description, 168 } 169 if !m.ExpiredAt.IsZero() { 170 rsp.ExpiredAt = &m.ExpiredAt 171 } 172 if !m.LastUsed.IsZero() { 173 rsp.LastUsed = &m.LastUsed 174 } 175 return rsp, nil 176 } 177 178 func DeleteByName(ctx context.Context, name string) error { 179 d := types.MustMgrDBExecutorFromContext(ctx) 180 acc := types.MustAccountFromContext(ctx) 181 182 m := &models.AccessKey{ 183 RelAccount: models.RelAccount{AccountID: acc.AccountID}, 184 AccessKeyInfo: models.AccessKeyInfo{Name: name}, 185 } 186 187 if err := m.DeleteByAccountIDAndName(d); err != nil { 188 if sqlx.DBErr(err).IsNotFound() { 189 return status.AccessKeyNotFound 190 } 191 return status.DatabaseError.StatusErr().WithDesc(err.Error()) 192 } 193 return nil 194 } 195 196 func List(ctx context.Context, r *ListReq) (*ListRsp, error) { 197 d := types.MustMgrDBExecutorFromContext(ctx) 198 m := &models.AccessKey{} 199 200 cond := r.Condition() 201 adds := r.Addition() 202 203 lst, err := m.List(d, cond, adds) 204 if err != nil { 205 return nil, status.DatabaseError.StatusErr().WithDesc(err.Error()) 206 } 207 cnt, err := m.Count(d, cond) 208 if err != nil { 209 return nil, status.DatabaseError.StatusErr().WithDesc(err.Error()) 210 } 211 212 ret := &ListRsp{Total: cnt} 213 214 for i := range lst { 215 ret.Data = append(ret.Data, NewListDataByModel(&lst[i])) 216 } 217 218 return ret, nil 219 } 220 221 func GetByName(ctx context.Context, name string) (*ListData, error) { 222 acc := types.MustAccountFromContext(ctx) 223 224 k := &models.AccessKey{ 225 RelAccount: models.RelAccount{AccountID: acc.AccountID}, 226 AccessKeyInfo: models.AccessKeyInfo{Name: name}, 227 } 228 229 err := k.FetchByAccountIDAndName(types.MustMgrDBExecutorFromContext(ctx)) 230 if err != nil { 231 if sqlx.DBErr(err).IsNotFound() { 232 return nil, status.AccessKeyNotFound 233 } 234 return nil, status.DatabaseError.StatusErr().WithDesc(err.Error()) 235 } 236 return NewListDataByModel(k), nil 237 } 238 239 func Validate(ctx context.Context, key string) (interface{}, error, bool) { 240 opId := httptransport.OperationIDFromContext(ctx) 241 kctx := &AccessKeyContext{} 242 243 err := kctx.UnmarshalText([]byte(key)) 244 if err != nil { 245 return nil, err, err != ErrInvalidPrefixOrPartCount 246 } 247 248 d := types.MustMgrDBExecutorFromContext(ctx) 249 m := &models.AccessKey{ 250 AccessKeyInfo: models.AccessKeyInfo{ 251 Rand: kctx.Rand, 252 }, 253 } 254 if err = m.FetchByRand(d); err != nil { 255 if sqlx.DBErr(err).IsNotFound() { 256 return nil, status.AccessKeyNotFound, true 257 } 258 return nil, status.DatabaseError.StatusErr().WithDesc(err.Error()), true 259 } 260 261 if kctx.GenTS.UTC().Second() != m.CreatedAt.UTC().Second() { 262 return nil, status.InvalidAccessKey, true 263 } 264 265 if !m.ExpiredAt.IsZero() && time.Now().UTC().After(m.ExpiredAt.Time) { 266 return nil, status.AccessKeyExpired, true 267 } 268 269 groupName, ok := gOperators[opId] 270 if !ok { 271 err = errors.Errorf("operator id is not registered: operator[%s]", opId) 272 return nil, status.AccessKeyPermissionDenied.StatusErr().WithDesc(err.Error()), true 273 } 274 perm, ok := m.Privileges[groupName] 275 if !ok { 276 err = errors.Errorf("no group permission: group[%s]", groupName) 277 return nil, status.AccessKeyPermissionDenied.StatusErr().WithDesc(err.Error()), true 278 } 279 opMeta := gOperatorGroups[groupName].Operators[opId] 280 if perm < opMeta.MinimalPerm { 281 err = errors.Errorf("no operator permission: operator[%s] group[%s] have perm[%s] need perm[%s]", 282 opId, groupName, perm, opMeta.MinimalPerm) 283 return nil, status.AccessKeyPermissionDenied.StatusErr().WithDesc(err.Error()), true 284 } 285 286 ts := base.Timestamp{Time: time.Now().UTC()} 287 if _, err = d.Exec( 288 builder.Update(d.T(m)).Set( 289 m.ColUpdatedAt().ValueBy(ts), 290 m.ColLastUsed().ValueBy(ts), 291 ).Where( 292 m.ColRand().Eq(kctx.Rand), 293 ), 294 ); err != nil { 295 logr.FromContext(ctx).Warn(errors.Wrap(err, "update access key last used")) 296 } 297 298 return m, nil, true 299 }