Files
im_server/server/db/adapter.go
T

395 lines
22 KiB
Go

// Package adapter contains the interfaces to be implemented by the database adapter
package adapter
import (
"encoding/json"
"time"
"github.com/tinode/chat/server/auth"
t "github.com/tinode/chat/server/store/types"
)
// Adapter is the interface that must be implemented by a database
// adapter. The current schema supports a single connection by database type.
type Adapter interface {
// General
// Open and configure the adapter
Open(config json.RawMessage) error
// Close the adapter
Close() error
// IsOpen checks if the adapter is ready for use
IsOpen() bool
// GetDbVersion returns current database version.
GetDbVersion() (int, error)
// CheckDbVersion checks if the actual database version matches adapter version.
CheckDbVersion() error
// GetName returns the name of the adapter
GetName() string
// SetMaxResults configures how many results can be returned in a single DB call.
SetMaxResults(val int) error
// CreateDb creates the database optionally dropping an existing database first.
CreateDb(reset bool) error
// UpgradeDb upgrades database to the current adapter version.
UpgradeDb() error
// Version returns adapter version
Version() int
// DB connection stats object.
Stats() any
// User management
// UserCreate creates user record
UserCreate(user *t.User) error
// UserGet returns record for a given user ID
UserGet(uid t.Uid) (*t.User, error)
// UserGetAll returns user records for a given list of user IDs
UserGetAll(ids ...t.Uid) ([]t.User, error)
// UserDelete deletes user record
UserDelete(uid t.Uid, hard bool) error
// UserUpdate updates user record
UserUpdate(uid t.Uid, update map[string]any) error
// UserUpdateTags adds, removes, or resets user's tags
UserUpdateTags(uid t.Uid, add, remove, reset []string) ([]string, error)
// UserGetByCred returns user ID for the given validated credential.
UserGetByCred(method, value string) (t.Uid, error)
// UserUnreadCount returns the total number of unread messages in all topics with
// the R permission. If read fails, the counts are still returned with the original
// user IDs but with the unread count undefined and non-nil error.
UserUnreadCount(ids ...t.Uid) (map[t.Uid]int, error)
// UserGetUnvalidated returns a list of no more than 'limit' uids who never logged in,
// have no validated credentials and which haven't been updated since 'lastUpdatedBefore'.
UserGetUnvalidated(lastUpdatedBefore time.Time, limit int) ([]t.Uid, error)
// Credential management
// CredUpsert adds or updates a credential record. Returns true if record was inserted, false if updated.
CredUpsert(cred *t.Credential) (bool, error)
// CredGetActive returns the currently active credential record for the given method.
CredGetActive(uid t.Uid, method string) (*t.Credential, error)
// CredGetAll returns credential records for the given user and method, validated only or all.
CredGetAll(uid t.Uid, method string, validatedOnly bool) ([]t.Credential, error)
// CredDel deletes credentials for the given method/value. If method is empty, deletes all
// user's credentials.
CredDel(uid t.Uid, method, value string) error
// CredConfirm marks given credential as validated.
CredConfirm(uid t.Uid, method string) error
// CredFail increments count of failed validation attepmts for the given credentials.
CredFail(uid t.Uid, method string) error
// Authentication management for the basic authentication scheme
// AuthGetUniqueRecord returns authentication record for a given unique value i.e. login.
AuthGetUniqueRecord(unique string) (t.Uid, auth.Level, []byte, time.Time, error)
// AuthGetRecord returns authentication record given user ID and method.
AuthGetRecord(user t.Uid, scheme string) (string, auth.Level, []byte, time.Time, error)
// AuthAddRecord creates new authentication record
AuthAddRecord(user t.Uid, scheme, unique string, authLvl auth.Level, secret []byte, expires time.Time) error
// AuthDelScheme deletes an existing authentication scheme for the user.
AuthDelScheme(user t.Uid, scheme string) error
// AuthDelAllRecords deletes all records of a given user.
AuthDelAllRecords(uid t.Uid) (int, error)
// AuthUpdRecord modifies an authentication record. Only non-default/non-zero values are updated.
AuthUpdRecord(user t.Uid, scheme, unique string, authLvl auth.Level, secret []byte, expires time.Time) error
// Topic management
// TopicCreate creates a topic
TopicCreate(topic *t.Topic) error
// TopicCreateP2P creates a p2p topic
TopicCreateP2P(initiator, invited *t.Subscription) error
// TopicGet loads a single topic by name, if it exists. If the topic does not exist the call returns (nil, nil)
TopicGet(topic string) (*t.Topic, error)
// TopicsForUser loads subscriptions for a given user. Reads public value.
// When the 'opts.IfModifiedSince' query is not nil the subscriptions with UpdatedAt > opts.IfModifiedSince
// are returned, where UpdatedAt can be either a subscription, a topic, or a user update timestamp.
// This is need in order to support paginagion of subscriptions: get subscriptions page by page
// from the oldest updates to most recent:
// 1. Client already has subscriptions with the latest update timestamp X.
// 2. Client asks for N updated subscriptions since X. The server returns N with updates between X and Y.
// 3. Client goes to step 1 with X := Y.
TopicsForUser(uid t.Uid, keepDeleted bool, opts *t.QueryOpt) ([]t.Subscription, error)
// UsersForTopic loads users' subscriptions for a given topic. Public is loaded.
UsersForTopic(topic string, keepDeleted bool, opts *t.QueryOpt) ([]t.Subscription, error)
// OwnTopics loads a slice of topic names where the user is the owner.
OwnTopics(uid t.Uid) ([]string, error)
// ChannelsForUser loads a slice of topic names where the user is a channel reader and notifications (P) are enabled.
ChannelsForUser(uid t.Uid) ([]string, error)
// TopicShare creates topic subscriptions.
TopicShare(topic string, subs []*t.Subscription) error
// TopicDelete deletes topic, subscriptions, messages.
TopicDelete(topic string, isChan, hard bool) error
// TopicUpdateOnMessage increments Topic's or User's SeqId value and updates TouchedAt timestamp.
TopicUpdateOnMessage(topic string, msg *t.Message) error
// TopicUpdateSubCnt refreshes denormalized topic subscriber count.
TopicUpdateSubCnt(topic string) error
// TopicUpdate updates topic record.
TopicUpdate(topic string, update map[string]any) error
// TopicOwnerChange updates topic's owner
TopicOwnerChange(topic string, newOwner t.Uid) error
// Topic subscriptions
// SubscriptionGet reads a subscription of a user to a topic
SubscriptionGet(topic string, user t.Uid, keepDeleted bool) (*t.Subscription, error)
// SubsForUser loads all subscriptions of a given user. Does NOT load Public or Private values,
// does not load deleted subscriptions.
SubsForUser(user t.Uid) ([]t.Subscription, error)
// SubsForTopic gets a list of subscriptions to a given topic.. Does NOT load Public value.
SubsForTopic(topic string, keepDeleted bool, opts *t.QueryOpt) ([]t.Subscription, error)
// SubsUpdate updates pasrt of a subscription object. Pass nil for fields which don't need to be updated
SubsUpdate(topic string, user t.Uid, update map[string]any) error
// SubsDelete deletes a single subscription
SubsDelete(topic string, user t.Uid) error
// Search
// Find searches for users or topics given a list of tags.
// - caller is the user or topic who is doing the searching, it will be skipped from results.
// - prefix if present will cause match rank highest in the results.
// - req is a list of required tag sets. Each set is a list of tags. The search will return
// all users/topics which have at least one tag from each set.
// - opt is a list of optional tags; if present the result will rank higher.
// - activeOnly if true will return only active subscriptions.
Find(caller, prefix string, req [][]string, opt []string, activeOnly bool) ([]t.Subscription, error)
// FindOne returns topic or user which matches the given tag.
FindOne(tag string) (string, error)
// Messages
// MessageSave saves message to database
MessageSave(msg *t.Message) error
// MessageGetAll returns messages matching the query
MessageGetAll(topic string, forUser t.Uid, opts *t.QueryOpt) ([]t.Message, error)
// MessageDeleteList marks messages as deleted.
// Soft- or Hard- is defined by forUser value: forUser.IsZero == true is hard.
MessageDeleteList(topic string, toDel *t.DelMessage) error
// MessageGetDeleted returns a list of deleted message Ids.
MessageGetDeleted(topic string, forUser t.Uid, opts *t.QueryOpt) ([]t.DelMessage, error)
// Devices (for push notifications)
// DeviceUpsert creates or updates a device record
DeviceUpsert(uid t.Uid, dev *t.DeviceDef) error
// DeviceGetAll returns all devices for a given set of users
DeviceGetAll(uid ...t.Uid) (map[t.Uid][]t.DeviceDef, int, error)
// DeviceDelete deletes a device record
DeviceDelete(uid t.Uid, deviceID string) error
// File upload records. The files are stored outside of the database.
// FileStartUpload initializes a file upload.
FileStartUpload(fd *t.FileDef) error
// FileFinishUpload marks file upload as completed, successfully or otherwise.
FileFinishUpload(fd *t.FileDef, success bool, size int64) (*t.FileDef, error)
// FileGet fetches a record of a specific file
FileGet(fid string) (*t.FileDef, error)
// FileDeleteUnused deletes records where UseCount is zero. If olderThan is non-zero, deletes
// unused records with UpdatedAt before olderThan.
// Returns array of FileDef.Location of deleted filerecords so actual files can be deleted too.
FileDeleteUnused(olderThan time.Time, limit int) ([]string, error)
// FileLinkAttachments connects given topic or message to the file record IDs from the list.
FileLinkAttachments(topic string, userId, msgId t.Uid, fids []string) error
// Persistent cache management.
// PCacheGet reads a persistent cache entry.
PCacheGet(key string) (string, error)
// PCacheUpsert creates or updates a persistent cache entry.
PCacheUpsert(key string, value string, failOnDuplicate bool) error
// PCacheDelete deletes a single persistent cache entry.
PCacheDelete(key string) error
// PCacheExpire expires older entries with the specified key prefix.
PCacheExpire(keyPrefix string, olderThan time.Time) error
// Testing
// GetTestDB returns a currently open database connection.
GetTestDB() any
}
// TenantAdapter is an optional capability implemented by tenant-aware database adapters.
// Keeping it separate lets adapters be migrated independently while tenant-aware entry
// points fail closed when the configured adapter does not support tenant resolution.
type TenantAdapter interface {
TenantGetByCode(code string) (*t.Tenant, error)
}
// TenantRoutedAdapter can return a tenant-specific adapter, e.g. for the
// "one tenant, one database" deployment model.
type TenantRoutedAdapter interface {
TenantAdapterFor(t.TenantID) (TenantBusinessAdapter, TenantAuthAdapter, error)
}
// TenantAuthAdapter is the tenant-aware authentication and credential capability.
// It is separate from Adapter while the remaining persistence domains are migrated.
type TenantAuthAdapter interface {
TenantAuthGetUniqueRecord(tenantID t.TenantID, unique string) (t.Uid, auth.Level, []byte, time.Time, error)
TenantAuthGetRecord(tenantID t.TenantID, user t.Uid, scheme string) (string, auth.Level, []byte, time.Time, error)
TenantAuthAddRecord(tenantID t.TenantID, user t.Uid, scheme, unique string, authLvl auth.Level,
secret []byte, expires time.Time) error
TenantAuthDelScheme(tenantID t.TenantID, user t.Uid, scheme string) error
TenantAuthUpdRecord(tenantID t.TenantID, user t.Uid, scheme, unique string, authLvl auth.Level,
secret []byte, expires time.Time) error
TenantUserGetByCred(tenantID t.TenantID, method, value string) (t.Uid, error)
TenantCredUpsert(tenantID t.TenantID, cred *t.Credential) (bool, error)
TenantCredGetActive(tenantID t.TenantID, uid t.Uid, method string) (*t.Credential, error)
TenantCredGetAll(tenantID t.TenantID, uid t.Uid, method string, validatedOnly bool) ([]t.Credential, error)
TenantCredDel(tenantID t.TenantID, uid t.Uid, method, value string) error
TenantCredConfirm(tenantID t.TenantID, uid t.Uid, method string) error
TenantCredFail(tenantID t.TenantID, uid t.Uid, method string) error
}
// TenantBusinessAdapter is the mandatory tenant-aware persistence capability
// used by request handling code. Adapters which do not implement it fail closed
// at the Store boundary instead of falling back to legacy unscoped queries.
type TenantBusinessAdapter interface {
// TenantBusinessReady reports whether every method below enforces tenant
// isolation in its database queries. Startup is rejected while false.
TenantBusinessReady() bool
TenantUserCreate(t.TenantID, *t.User) error
TenantUserGet(t.TenantID, t.Uid) (*t.User, error)
TenantUserGetAll(t.TenantID, ...t.Uid) ([]t.User, error)
TenantUserDelete(t.TenantID, t.Uid, bool) error
TenantUserUpdate(t.TenantID, t.Uid, map[string]any) error
TenantUserUpdateTags(t.TenantID, t.Uid, []string, []string, []string) ([]string, error)
TenantUserUnreadCount(t.TenantID, ...t.Uid) (map[t.Uid]int, error)
TenantUserGetUnvalidated(t.TenantID, time.Time, int) ([]t.Uid, error)
TenantTopicCreate(t.TenantID, *t.Topic) error
TenantTopicCreateP2P(t.TenantID, *t.Subscription, *t.Subscription) error
TenantTopicGet(t.TenantID, string) (*t.Topic, error)
TenantTopicsForUser(t.TenantID, t.Uid, bool, *t.QueryOpt) ([]t.Subscription, error)
TenantUsersForTopic(t.TenantID, string, bool, *t.QueryOpt) ([]t.Subscription, error)
TenantOwnTopics(t.TenantID, t.Uid) ([]string, error)
TenantChannelsForUser(t.TenantID, t.Uid) ([]string, error)
TenantTopicShare(t.TenantID, string, []*t.Subscription) error
TenantTopicDelete(t.TenantID, string, bool, bool) error
TenantTopicUpdateOnMessage(t.TenantID, string, *t.Message) error
TenantTopicUpdateSubCnt(t.TenantID, string) error
TenantTopicUpdate(t.TenantID, string, map[string]any) error
TenantTopicOwnerChange(t.TenantID, string, t.Uid) error
TenantSubscriptionGet(t.TenantID, string, t.Uid, bool) (*t.Subscription, error)
TenantSubsForUser(t.TenantID, t.Uid) ([]t.Subscription, error)
TenantSubsForTopic(t.TenantID, string, bool, *t.QueryOpt) ([]t.Subscription, error)
TenantSubsUpdate(t.TenantID, string, t.Uid, map[string]any) error
TenantSubsDelete(t.TenantID, string, t.Uid) error
TenantFind(t.TenantID, string, string, [][]string, []string, bool) ([]t.Subscription, error)
TenantFindOne(t.TenantID, string) (string, error)
TenantMessageSave(t.TenantID, *t.Message) error
TenantMessageGetByClientID(t.TenantID, t.Uid, string) (*t.Message, error)
TenantMessageGetOne(t.TenantID, string, int) (*t.Message, error)
TenantMessageRecall(t.TenantID, string, int, t.Uid, int, time.Time) (*t.Message, error)
TenantMessageEdit(t.TenantID, string, int, t.Uid, int, t.KVMap, any, string, time.Time) (*t.Message, error)
TenantMessageGetAll(t.TenantID, string, t.Uid, *t.QueryOpt) ([]t.Message, error)
TenantMessageDeleteList(t.TenantID, string, *t.DelMessage) error
TenantMessageGetDeleted(t.TenantID, string, t.Uid, *t.QueryOpt) ([]t.DelMessage, error)
TenantMessageSearch(t.TenantID, string, t.Uid, string, string, string, int) ([]t.Message, string, error)
TenantCallCreate(t.TenantID, *t.CallRecord) error
TenantCallAccept(t.TenantID, string, int, time.Time) error
TenantCallFinish(t.TenantID, string, int, string, int64, time.Time) error
TenantCallList(t.TenantID, t.Uid, string, string, int) ([]t.CallRecord, string, error)
TenantWorkbenchModules(t.TenantID, t.Uid) ([]t.WorkbenchModule, int, error)
TenantOrganizationDepartments(t.TenantID, t.Uid, string, string, int) ([]t.OrganizationDepartment, string, error)
TenantOrganizationMembers(t.TenantID, t.Uid, string, string, int) ([]t.OrganizationMember, string, error)
TenantOrganizationSearch(t.TenantID, t.Uid, string, string, string, int) ([]t.OrganizationSearchItem, string, error)
TenantMeetingCreate(t.TenantID, t.Uid, *t.Meeting, []t.Uid) (*t.Meeting, error)
TenantMeetingGet(t.TenantID, t.Uid, string) (*t.Meeting, error)
TenantMeetingUpdate(t.TenantID, t.Uid, string, t.MeetingPatch, string) (*t.Meeting, error)
TenantMeetingCancel(t.TenantID, t.Uid, string, string) (*t.Meeting, error)
TenantMeetingRemind(t.TenantID, t.Uid, string, string) ([]t.MeetingParticipant, error)
TenantMeetingRespond(t.TenantID, t.Uid, string, string, int) (*t.Meeting, error)
TenantDailyReportList(t.TenantID, t.Uid, string, string, string, string, string, int) ([]t.DailyReport, string, error)
TenantDailyReportCreateDraft(t.TenantID, t.Uid, string, time.Time) (*t.DailyReport, error)
TenantDailyReportGet(t.TenantID, t.Uid, string) (*t.DailyReport, error)
TenantDailyReportSaveDraft(t.TenantID, t.Uid, string, string, []string, int) (*t.DailyReport, error)
TenantDailyReportSubmit(t.TenantID, t.Uid, string, int) (*t.DailyReport, error)
TenantDailyReportStats(t.TenantID, t.Uid, time.Time, string) (*t.DailyReportStats, error)
TenantDailyReportRemind(t.TenantID, t.Uid, time.Time, []t.Uid, string) ([]t.Uid, error)
TenantMonthlyReportList(t.TenantID, t.Uid, string, string, string, string, string, int) ([]t.MonthlyReport, string, error)
TenantMonthlyReportCreateDraft(t.TenantID, t.Uid, string, string) (*t.MonthlyReport, error)
TenantMonthlyReportGet(t.TenantID, t.Uid, string) (*t.MonthlyReport, error)
TenantMonthlyReportSaveDraft(t.TenantID, t.Uid, string, string, string, []string, int) (*t.MonthlyReport, error)
TenantMonthlyReportSubmit(t.TenantID, t.Uid, string, int) (*t.MonthlyReport, error)
TenantMonthlyReportStats(t.TenantID, t.Uid, string, string) (*t.MonthlyReportStats, error)
TenantMailboxConfigGet(t.TenantID, t.Uid) (*t.MailboxConfig, error)
TenantMailboxSubmissionCreate(t.TenantID, t.Uid, *t.MailboxSubmission, string) (*t.MailboxSubmission, error)
TenantUserProfileGet(t.TenantID, t.Uid) (*t.UserProfile, error)
TenantUserProfileUpdate(t.TenantID, t.Uid, t.UserProfilePatch) (*t.UserProfile, error)
TenantUserPrivacyGet(t.TenantID, t.Uid) (*t.UserPrivacySettings, error)
TenantUserPrivacyUpdate(t.TenantID, t.Uid, t.UserPrivacyPatch) (*t.UserPrivacySettings, error)
TenantAppReleaseLatest(t.TenantID, string, string) (*t.AppRelease, error)
TenantDevicePushTokenUpsert(t.TenantID, *t.DevicePushToken) (*t.DevicePushToken, error)
TenantRiskChallengeCreate(t.TenantID, *t.RiskChallenge) (*t.RiskChallenge, error)
TenantRiskChallengeConfirm(t.TenantID, t.Uid, string, string, string, time.Time) (*t.RiskChallenge, error)
TenantAbuseReasons(t.TenantID, string) ([]t.AbuseReason, error)
TenantAbuseReportCreate(t.TenantID, *t.AbuseReport, string) (*t.AbuseReport, error)
TenantFeedbackConfigGet(t.TenantID, t.Uid) (*t.FeedbackConfig, error)
TenantFeedbackCreate(t.TenantID, *t.FeedbackSubmission, string) (*t.FeedbackSubmission, error)
TenantFavoriteCreate(t.TenantID, *t.FavoriteItem) (*t.FavoriteItem, error)
TenantFavoriteList(t.TenantID, t.Uid, string, string, string, int) ([]t.FavoriteItem, string, error)
TenantFavoriteGet(t.TenantID, t.Uid, string) (*t.FavoriteItem, error)
TenantFavoriteDelete(t.TenantID, t.Uid, string) error
TenantDeviceUpsert(t.TenantID, t.Uid, *t.DeviceDef) error
TenantDeviceGetAll(t.TenantID, ...t.Uid) (map[t.Uid][]t.DeviceDef, int, error)
TenantDeviceDelete(t.TenantID, t.Uid, string) error
TenantFileStartUpload(t.TenantID, *t.FileDef) error
TenantFileFinishUpload(t.TenantID, *t.FileDef, bool, int64) (*t.FileDef, error)
TenantFileGet(t.TenantID, string) (*t.FileDef, error)
TenantFileDeleteUnused(t.TenantID, time.Time, int) ([]string, error)
TenantFileLinkAttachments(t.TenantID, string, t.Uid, t.Uid, []string) error
TenantFileUserHasAccess(t.TenantID, string, t.Uid) (bool, error)
TenantPCacheGet(t.TenantID, string) (string, error)
TenantPCacheUpsert(t.TenantID, string, string, bool) error
TenantPCacheDelete(t.TenantID, string) error
TenantPCacheExpire(t.TenantID, string, time.Time) error
TenantVerificationCreate(t.TenantID, *t.VerificationCode) error
TenantVerificationGet(t.TenantID, string) (*t.VerificationCode, error)
TenantVerificationGetActive(t.TenantID, string, string, time.Time) (*t.VerificationCode, error)
TenantVerificationFail(t.TenantID, string, t.VerificationState) error
TenantVerificationMarkVerified(t.TenantID, string, string, time.Time) error
TenantVerificationConsume(t.TenantID, string, string, time.Time) error
TenantUserSecurityGetAuthVersion(t.TenantID, t.Uid) (int, error)
TenantUserSecurityBumpAuthVersion(t.TenantID, t.Uid, time.Time) (int, error)
TenantRealNameGet(t.TenantID, t.Uid) (*t.RealNameVerification, error)
TenantRealNameCreate(t.TenantID, *t.RealNameVerification) error
TenantActionTokenCreate(t.TenantID, *t.ActionToken) error
TenantActionTokenConsume(t.TenantID, t.Uid, string, string, string, time.Time) (*t.ActionToken, error)
TenantIndependentPasswordGet(t.TenantID, t.Uid, string) (*t.IndependentPassword, error)
TenantIndependentPasswordUpsert(t.TenantID, *t.IndependentPassword) error
TenantIndependentPasswordUpdateAttempts(t.TenantID, t.Uid, string, int, *time.Time, time.Time) error
TenantYouthModeGet(t.TenantID, t.Uid) (*t.YouthModeSettings, error)
TenantYouthModeSet(t.TenantID, t.Uid, bool, int, time.Time) (*t.YouthModeSettings, error)
TenantSystemNotificationCreate(t.TenantID, *t.SystemNotification) error
TenantSystemNotificationList(t.TenantID, t.Uid, string, int) ([]t.SystemNotification, string, error)
TenantSystemNotificationMarkRead(t.TenantID, t.Uid, string, time.Time) error
TenantFriendRequestCreate(t.TenantID, *t.FriendRequest) (*t.FriendRequest, error)
TenantFriendRequestList(t.TenantID, t.Uid, string, string, string, int) ([]t.FriendRequestListItem, string, error)
TenantFriendRequestLatestBetween(t.TenantID, t.Uid, t.Uid) (*t.FriendRequest, error)
TenantFriendRequestAccept(t.TenantID, string, t.Uid, time.Time) (*t.FriendRequest, error)
TenantFriendRequestResolve(t.TenantID, string, t.Uid, string, time.Time) (*t.FriendRequest, error)
TenantFriendshipUpsertPair(t.TenantID, t.Uid, t.Uid, string, time.Time) error
TenantFriendshipList(t.TenantID, t.Uid, string, string, string, int) ([]t.FriendListItem, string, error)
TenantFriendshipGet(t.TenantID, t.Uid, t.Uid) (*t.Friendship, error)
TenantFriendshipUpdateRemark(t.TenantID, t.Uid, t.Uid, string, time.Time) (*t.Friendship, error)
TenantFriendshipDeletePair(t.TenantID, t.Uid, t.Uid, time.Time) error
TenantUserBlockGet(t.TenantID, t.Uid, t.Uid) (*t.UserBlock, error)
TenantUserBlockList(t.TenantID, t.Uid, string, int) ([]t.BlockListItem, string, error)
TenantUserBlockUpsert(t.TenantID, t.Uid, t.Uid, time.Time) (*t.UserBlock, error)
TenantUserBlockDelete(t.TenantID, t.Uid, t.Uid) error
TenantUserSearch(t.TenantID, t.Uid, string, string, string, int) ([]t.User, string, error)
}