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  }