fixes #34 「动态指令」多行指令会被当作一行执行

fixes #32 会话无法维持,1分钟左右自动断开
fixes #31 更新"资产"会清空"标签"
fixes #13 建议添加用户权限功能、隐藏授权账户信息
This commit is contained in:
dushixiang
2021-01-18 18:28:33 +08:00
parent c93e03e951
commit c98b3adbe6
26 changed files with 642 additions and 378 deletions

View File

@ -49,7 +49,7 @@ func LoginEndpoint(c echo.Context) error {
return Fail(c, -1, "您输入的账号或密码不正确")
}
if user.TOTPSecret != "" {
if user.TOTPSecret != "" && user.TOTPSecret != "-" {
return Fail(c, 0, "")
}

View File

@ -1,9 +1,11 @@
package api
import (
"fmt"
"github.com/labstack/echo/v4"
"github.com/sirupsen/logrus"
"next-terminal/pkg/global"
"next-terminal/pkg/model"
"strings"
"time"
)
@ -12,6 +14,12 @@ func ErrorHandler(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
if err := next(c); err != nil {
if he, ok := err.(*echo.HTTPError); ok {
message := fmt.Sprintf("%v", he.Message)
return Fail(c, he.Code, message)
}
return Fail(c, 0, err.Error())
}
return nil
@ -21,9 +29,6 @@ func ErrorHandler(next echo.HandlerFunc) echo.HandlerFunc {
func Auth(next echo.HandlerFunc) echo.HandlerFunc {
urls := []string{"download", "recording", "login", "static", "favicon", "logo"}
permissionUrls := H{
"/users": "admin",
}
return func(c echo.Context) error {
// 路由拦截 - 登录身份、资源权限判断等
@ -43,14 +48,6 @@ func Auth(next echo.HandlerFunc) echo.HandlerFunc {
return Fail(c, 401, "您的登录信息已失效,请重新登录后再试。")
}
for url := range permissionUrls {
if strings.HasPrefix(c.Request().RequestURI, url) {
if authorization.(Authorization).User.Type != permissionUrls[url] {
return Fail(c, 403, "permission denied")
}
}
}
if authorization.(Authorization).Remember {
// 记住登录有效期两周
global.Cache.Set(token, authorization, time.Hour*time.Duration(24*14))
@ -61,3 +58,16 @@ func Auth(next echo.HandlerFunc) echo.HandlerFunc {
return next(c)
}
}
func Admin(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
account, _ := GetCurrentAccount(c)
if account.Type != model.TypeAdmin {
return Fail(c, 403, "permission denied")
}
return next(c)
}
}

View File

@ -3,7 +3,6 @@ package api
import (
"github.com/labstack/echo/v4"
"next-terminal/pkg/model"
"strings"
)
type RU struct {
@ -12,8 +11,14 @@ type RU struct {
ResourceIds []string `json:"resourceIds"`
}
func ResourceGetAssignEndPoint(c echo.Context) error {
resourceId := c.Param("id")
type UR struct {
ResourceId string `json:"resourceId"`
ResourceType string `json:"resourceType"`
UserIds []string `json:"userIds"`
}
func RSGetSharersEndPoint(c echo.Context) error {
resourceId := c.QueryParam("resourceId")
userIds, err := model.FindUserIdsByResourceId(resourceId)
if err != nil {
return err
@ -21,13 +26,15 @@ func ResourceGetAssignEndPoint(c echo.Context) error {
return Success(c, userIds)
}
func ResourceOverwriteAssignEndPoint(c echo.Context) error {
resourceId := c.Param("id")
userIds := c.QueryParam("userIds")
resourceType := c.QueryParam("type")
uIds := strings.Split(userIds, ",")
func RSOverwriteSharersEndPoint(c echo.Context) error {
var ur UR
if err := c.Bind(&ur); err != nil {
return err
}
model.OverwriteUserIdsByResourceId(resourceId, resourceType, uIds)
if err := model.OverwriteUserIdsByResourceId(ur.ResourceId, ur.ResourceType, ur.UserIds); err != nil {
return err
}
return Success(c, "")
}

View File

@ -44,20 +44,22 @@ func SetupRoutes() *echo.Echo {
users := e.Group("/users")
{
users.POST("", UserCreateEndpoint)
users.POST("", Admin(UserCreateEndpoint))
users.GET("/paging", UserPagingEndpoint)
users.PUT("/:id", UserUpdateEndpoint)
users.DELETE("/:id", UserDeleteEndpoint)
users.GET("/:id", UserGetEndpoint)
users.PUT("/:id", Admin(UserUpdateEndpoint))
users.DELETE("/:id", Admin(UserDeleteEndpoint))
users.GET("/:id", Admin(UserGetEndpoint))
users.POST("/:id/change-password", Admin(UserChangePasswordEndpoint))
users.POST("/:id/reset-totp", Admin(UserResetTotpEndpoint))
}
userGroups := e.Group("/user-groups")
{
userGroups.POST("", UserGroupCreateEndpoint)
userGroups.GET("/paging", UserGroupPagingEndpoint)
userGroups.PUT("/:id", UserGroupUpdateEndpoint)
userGroups.DELETE("/:id", UserGroupDeleteEndpoint)
userGroups.GET("/:id", UserGroupGetEndpoint)
userGroups.POST("", Admin(UserGroupCreateEndpoint))
userGroups.GET("/paging", Admin(UserGroupPagingEndpoint))
userGroups.PUT("/:id", Admin(UserGroupUpdateEndpoint))
userGroups.DELETE("/:id", Admin(UserGroupDeleteEndpoint))
userGroups.GET("/:id", Admin(UserGroupGetEndpoint))
//userGroups.POST("/:id/members", UserGroupAddMembersEndpoint)
//userGroups.DELETE("/:id/members/:memberId", UserGroupDelMembersEndpoint)
}
@ -71,7 +73,7 @@ func SetupRoutes() *echo.Echo {
assets.PUT("/:id", AssetUpdateEndpoint)
assets.DELETE("/:id", AssetDeleteEndpoint)
assets.GET("/:id", AssetGetEndpoint)
assets.POST("/:id/change-owner", AssetChangeOwnerEndpoint)
assets.POST("/:id/change-owner", Admin(AssetChangeOwnerEndpoint))
}
e.GET("/tags", AssetTagsEndpoint)
@ -83,7 +85,7 @@ func SetupRoutes() *echo.Echo {
commands.PUT("/:id", CommandUpdateEndpoint)
commands.DELETE("/:id", CommandDeleteEndpoint)
commands.GET("/:id", CommandGetEndpoint)
commands.POST("/:id/change-owner", CommandChangeOwnerEndpoint)
commands.POST("/:id/change-owner", Admin(CommandChangeOwnerEndpoint))
}
credentials := e.Group("/credentials")
@ -94,7 +96,7 @@ func SetupRoutes() *echo.Echo {
credentials.PUT("/:id", CredentialUpdateEndpoint)
credentials.DELETE("/:id", CredentialDeleteEndpoint)
credentials.GET("/:id", CredentialGetEndpoint)
credentials.POST("/:id/change-owner", CredentialChangeOwnerEndpoint)
credentials.POST("/:id/change-owner", Admin(CredentialChangeOwnerEndpoint))
}
sessions := e.Group("/sessions")
@ -102,7 +104,7 @@ func SetupRoutes() *echo.Echo {
sessions.POST("", SessionCreateEndpoint)
sessions.GET("/paging", SessionPagingEndpoint)
sessions.POST("/:id/content", SessionContentEndpoint)
sessions.POST("/:id/discontent", SessionDiscontentEndpoint)
sessions.POST("/:id/discontent", Admin(SessionDiscontentEndpoint))
sessions.POST("/:id/resize", SessionResizeEndpoint)
sessions.POST("/:id/upload", SessionUploadEndpoint)
sessions.GET("/:id/download", SessionDownloadEndpoint)
@ -111,20 +113,20 @@ func SetupRoutes() *echo.Echo {
sessions.DELETE("/:id/rmdir", SessionRmDirEndpoint)
sessions.DELETE("/:id/rm", SessionRmEndpoint)
sessions.DELETE("/:id", SessionDeleteEndpoint)
sessions.GET("/:id/recording", SessionRecordingEndpoint)
sessions.GET("/:id/recording", Admin(SessionRecordingEndpoint))
sessions.GET("/:id", SessionGetEndpoint)
}
resources := e.Group("/resources")
resourceSharers := e.Group("/resource-sharers")
{
resources.GET("/:id/assign", ResourceGetAssignEndPoint)
resources.POST("/:id/assign", ResourceOverwriteAssignEndPoint)
resources.POST("/remove", ResourceRemoveByUserIdAssignEndPoint)
resources.POST("/add", ResourceAddByUserIdAssignEndPoint)
resourceSharers.GET("/sharers", RSGetSharersEndPoint)
resourceSharers.POST("/overwrite-sharers", RSOverwriteSharersEndPoint)
resourceSharers.POST("/remove-resources", Admin(ResourceRemoveByUserIdAssignEndPoint))
resourceSharers.POST("/add-resources", Admin(ResourceAddByUserIdAssignEndPoint))
}
e.GET("/properties", PropertyGetEndpoint)
e.PUT("/properties", PropertyUpdateEndpoint)
e.PUT("/properties", Admin(PropertyUpdateEndpoint))
e.GET("/overview/counter", OverviewCounterEndPoint)
e.GET("/overview/sessions", OverviewSessionPoint)
@ -174,15 +176,16 @@ func GetCurrentAccount(c echo.Context) (model.User, bool) {
}
func HasPermission(c echo.Context, owner string) bool {
// 检测是否为创建者
// 检测是否登录
account, found := GetCurrentAccount(c)
if !found {
return false
}
// 检测是否为管理人员
if model.TypeAdmin == account.Type {
return true
}
// 检测是否为所有者
if owner == account.ID {
return true
}

View File

@ -103,6 +103,15 @@ func CloseSessionById(sessionId string, code int, reason string) {
CloseSessionByWebSocket(tun.WebSocket, code, reason)
}
s, err := model.FindSessionById(sessionId)
if err != nil {
return
}
if s.Status == model.Disconnected {
return
}
global.Store.Del(sessionId)
session := model.Session{}
session.ID = sessionId

View File

@ -27,7 +27,6 @@ func UserCreateEndpoint(c echo.Context) error {
if err := model.CreateNewUser(&item); err != nil {
return err
}
return Success(c, item)
}
@ -89,3 +88,27 @@ func UserGetEndpoint(c echo.Context) error {
return Success(c, item)
}
func UserChangePasswordEndpoint(c echo.Context) error {
id := c.Param("id")
password := c.QueryParam("password")
passwd, err := utils.Encoder.Encode([]byte(password))
if err != nil {
return err
}
u := &model.User{
Password: string(passwd),
}
model.UpdateUserById(u, id)
return Success(c, "")
}
func UserResetTotpEndpoint(c echo.Context) error {
id := c.Param("id")
u := &model.User{
TOTPSecret: "-",
}
model.UpdateUserById(u, id)
return Success(c, "")
}

View File

@ -49,11 +49,11 @@ func FindAllAsset() (o []Asset, err error) {
}
func FindAssetByConditions(protocol string, account User) (o []Asset, err error) {
db := global.DB.Table("assets").Select("assets.id,assets.name,assets.ip,assets.port,assets.protocol,assets.active,assets.owner,assets.created, users.nickname as owner_name,COUNT(resources.user_id) as sharer_count").Joins("left join users on assets.owner = users.id").Joins("left join resources on assets.id = resources.resource_id").Group("assets.id")
db := global.DB.Table("assets").Select("assets.id,assets.name,assets.ip,assets.port,assets.protocol,assets.active,assets.owner,assets.created, users.nickname as owner_name,COUNT(resource_sharers.user_id) as sharer_count").Joins("left join users on assets.owner = users.id").Joins("left join resource_sharers on assets.id = resource_sharers.resource_id").Group("assets.id")
if TypeUser == account.Type {
owner := account.ID
db = db.Where("assets.owner = ? or resources.user_id = ?", owner, owner)
db = db.Where("assets.owner = ? or resource_sharers.user_id = ?", owner, owner)
}
if len(protocol) > 0 {
@ -64,21 +64,21 @@ func FindAssetByConditions(protocol string, account User) (o []Asset, err error)
}
func FindPageAsset(pageIndex, pageSize int, name, protocol, tags string, account User, owner, sharer string) (o []AssetVo, total int64, err error) {
db := global.DB.Table("assets").Select("assets.id,assets.name,assets.ip,assets.port,assets.protocol,assets.active,assets.owner,assets.created, users.nickname as owner_name,COUNT(resources.user_id) as sharer_count").Joins("left join users on assets.owner = users.id").Joins("left join resources on assets.id = resources.resource_id").Group("assets.id")
dbCounter := global.DB.Table("assets").Select("DISTINCT assets.id").Joins("left join resources on assets.id = resources.resource_id")
db := global.DB.Table("assets").Select("assets.id,assets.name,assets.ip,assets.port,assets.protocol,assets.active,assets.owner,assets.created, users.nickname as owner_name,COUNT(resource_sharers.user_id) as sharer_count").Joins("left join users on assets.owner = users.id").Joins("left join resource_sharers on assets.id = resource_sharers.resource_id").Group("assets.id")
dbCounter := global.DB.Table("assets").Select("DISTINCT assets.id").Joins("left join resource_sharers on assets.id = resource_sharers.resource_id")
if TypeUser == account.Type {
owner := account.ID
db = db.Where("assets.owner = ? or resources.user_id = ?", owner, owner)
dbCounter = dbCounter.Where("assets.owner = ? or resources.user_id = ?", owner, owner)
db = db.Where("assets.owner = ? or resource_sharers.user_id = ?", owner, owner)
dbCounter = dbCounter.Where("assets.owner = ? or resource_sharers.user_id = ?", owner, owner)
} else {
if len(owner) > 0 {
db = db.Where("assets.owner = ?", owner)
dbCounter = dbCounter.Where("assets.owner = ?", owner)
}
if len(sharer) > 0 {
db = db.Where("resources.user_id = ?", sharer)
dbCounter = dbCounter.Where("resources.user_id = ?", sharer)
db = db.Where("resource_sharers.user_id = ?", sharer)
dbCounter = dbCounter.Where("resource_sharers.user_id = ?", sharer)
}
}

View File

@ -29,13 +29,13 @@ func (r *Command) TableName() string {
func FindPageCommand(pageIndex, pageSize int, name, content string, account User) (o []CommandVo, total int64, err error) {
db := global.DB.Table("commands").Select("commands.id,commands.name,commands.content,commands.owner,commands.created, users.nickname as owner_name,COUNT(resources.user_id) as sharer_count").Joins("left join users on commands.owner = users.id").Joins("left join resources on commands.id = resources.resource_id").Group("commands.id")
dbCounter := global.DB.Table("commands").Select("DISTINCT commands.id").Joins("left join resources on commands.id = resources.resource_id")
db := global.DB.Table("commands").Select("commands.id,commands.name,commands.content,commands.owner,commands.created, users.nickname as owner_name,COUNT(resource_sharers.user_id) as sharer_count").Joins("left join users on commands.owner = users.id").Joins("left join resource_sharers on commands.id = resource_sharers.resource_id").Group("commands.id")
dbCounter := global.DB.Table("commands").Select("DISTINCT commands.id").Joins("left join resource_sharers on commands.id = resource_sharers.resource_id")
if TypeUser == account.Type {
owner := account.ID
db = db.Where("commands.owner = ? or resources.user_id = ?", owner, owner)
dbCounter = dbCounter.Where("commands.owner = ? or resources.user_id = ?", owner, owner)
db = db.Where("commands.owner = ? or resource_sharers.user_id = ?", owner, owner)
dbCounter = dbCounter.Where("commands.owner = ? or resource_sharers.user_id = ?", owner, owner)
}
if len(name) > 0 {

View File

@ -44,22 +44,22 @@ type CredentialSimpleVo struct {
}
func FindAllCredential(account User) (o []CredentialSimpleVo, err error) {
db := global.DB.Table("credentials").Select("DISTINCT credentials.id,credentials.name").Joins("left join resources on credentials.id = resources.resource_id")
db := global.DB.Table("credentials").Select("DISTINCT credentials.id,credentials.name").Joins("left join resource_sharers on credentials.id = resource_sharers.resource_id")
if account.Type == TypeUser {
db = db.Where("credentials.owner = ? or resources.user_id = ?", account.ID, account.ID)
db = db.Where("credentials.owner = ? or resource_sharers.user_id = ?", account.ID, account.ID)
}
err = db.Find(&o).Error
return
}
func FindPageCredential(pageIndex, pageSize int, name string, account User) (o []CredentialVo, total int64, err error) {
db := global.DB.Table("credentials").Select("credentials.id,credentials.name,credentials.type,credentials.username,credentials.owner,credentials.created,users.nickname as owner_name,COUNT(resources.user_id) as sharer_count").Joins("left join users on credentials.owner = users.id").Joins("left join resources on credentials.id = resources.resource_id").Group("credentials.id")
dbCounter := global.DB.Table("credentials").Select("DISTINCT credentials.id").Joins("left join resources on credentials.id = resources.resource_id")
db := global.DB.Table("credentials").Select("credentials.id,credentials.name,credentials.type,credentials.username,credentials.owner,credentials.created,users.nickname as owner_name,COUNT(resource_sharers.user_id) as sharer_count").Joins("left join users on credentials.owner = users.id").Joins("left join resource_sharers on credentials.id = resource_sharers.resource_id").Group("credentials.id")
dbCounter := global.DB.Table("credentials").Select("DISTINCT credentials.id").Joins("left join resource_sharers on credentials.id = resource_sharers.resource_id")
if TypeUser == account.Type {
owner := account.ID
db = db.Where("credentials.owner = ? or resources.user_id = ?", owner, owner)
dbCounter = dbCounter.Where("credentials.owner = ? or resources.user_id = ?", owner, owner)
db = db.Where("credentials.owner = ? or resource_sharers.user_id = ?", owner, owner)
dbCounter = dbCounter.Where("credentials.owner = ? or resource_sharers.user_id = ?", owner, owner)
}
if len(name) > 0 {

View File

@ -0,0 +1,128 @@
package model
import (
"github.com/labstack/echo/v4"
"gorm.io/gorm"
"next-terminal/pkg/global"
"next-terminal/pkg/utils"
)
type ResourceSharer struct {
ID string `gorm:"primary_key" json:"name"`
ResourceId string `json:"resourceId"`
ResourceType string `json:"resourceType"`
UserId string `json:"userId"`
}
func (r *ResourceSharer) TableName() string {
return "resource_sharers"
}
func FindUserIdsByResourceId(resourceId string) (r []string, err error) {
db := global.DB
err = db.Table("resource_sharers").Select("user_id").Where("resource_id = ?", resourceId).Find(&r).Error
if r == nil {
r = make([]string, 0)
}
return
}
func OverwriteUserIdsByResourceId(resourceId, resourceType string, userIds []string) (err error) {
db := global.DB.Begin()
var owner string
// 检查资产是否存在
switch resourceType {
case "asset":
resource := Asset{}
err = db.Where("id = ?", resourceId).First(&resource).Error
owner = resource.Owner
case "command":
resource := Command{}
err = db.Where("id = ?", resourceId).First(&resource).Error
owner = resource.Owner
case "credential":
resource := Credential{}
err = db.Where("id = ?", resourceId).First(&resource).Error
owner = resource.Owner
}
if err == gorm.ErrRecordNotFound {
return echo.NewHTTPError(404, "资源「"+resourceId+"」不存在")
}
for i := range userIds {
if owner == userIds[i] {
return echo.NewHTTPError(400, "参数错误")
}
}
db.Where("resource_id = ?", resourceId).Delete(&ResourceSharer{})
for i := range userIds {
userId := userIds[i]
if len(userId) == 0 {
continue
}
id := utils.Sign([]string{resourceId, resourceType, userId})
resource := &ResourceSharer{
ID: id,
ResourceId: resourceId,
ResourceType: resourceType,
UserId: userId,
}
err = db.Create(resource).Error
if err != nil {
return err
}
}
db.Commit()
return nil
}
func DeleteByUserIdAndResourceTypeAndResourceIdIn(userId, resourceType string, resourceIds []string) error {
return global.DB.Where("user_id = ? and resource_type = ? and resource_id in ?", userId, resourceType, resourceIds).Delete(&ResourceSharer{}).Error
}
func AddSharerResources(userId, resourceType string, resourceIds []string) error {
return global.DB.Transaction(func(tx *gorm.DB) (err error) {
for i := range resourceIds {
resourceId := resourceIds[i]
var owner string
// 检查资产是否存在
switch resourceType {
case "asset":
resource := Asset{}
err = tx.Where("id = ?", resourceId).First(&resource).Error
owner = resource.Owner
case "command":
resource := Command{}
err = tx.Where("id = ?", resourceId).First(&resource).Error
owner = resource.Owner
case "credential":
resource := Credential{}
err = tx.Where("id = ?", resourceId).First(&resource).Error
owner = resource.Owner
}
if owner == userId {
return echo.NewHTTPError(400, "参数错误")
}
id := utils.Sign([]string{resourceId, resourceType, userId})
resource := &ResourceSharer{
ID: id,
ResourceId: resourceId,
ResourceType: resourceType,
UserId: userId,
}
err = tx.Create(resource).Error
if err != nil {
return err
}
}
return nil
})
}

View File

@ -1,73 +0,0 @@
package model
import (
"gorm.io/gorm"
"next-terminal/pkg/global"
"next-terminal/pkg/utils"
)
type Resource struct {
ID string `gorm:"primary_key" json:"name"`
ResourceId string `json:"resourceId"`
ResourceType string `json:"resourceType"`
UserId string `json:"userId"`
}
func (r *Resource) TableName() string {
return "resources"
}
func FindUserIdsByResourceId(resourceId string) (r []string, err error) {
db := global.DB
err = db.Table("resources").Select("user_id").Where("resource_id = ?", resourceId).Find(&r).Error
if r == nil {
r = make([]string, 0)
}
return
}
func OverwriteUserIdsByResourceId(resourceId, resourceType string, userIds []string) {
db := global.DB.Begin()
db.Where("resource_id = ?", resourceId).Delete(&Resource{})
for i := range userIds {
userId := userIds[i]
if len(userId) == 0 {
continue
}
id := utils.Sign([]string{resourceId, resourceType, userId})
resource := &Resource{
ID: id,
ResourceId: resourceId,
ResourceType: resourceType,
UserId: userId,
}
_ = db.Create(resource).Error
}
db.Commit()
}
func DeleteByUserIdAndResourceTypeAndResourceIdIn(userId, resourceType string, resourceIds []string) error {
return global.DB.Where("user_id = ? and resource_type = ? and resource_id in ?", userId, resourceType, resourceIds).Delete(&Resource{}).Error
}
func AddSharerResources(userId, resourceType string, resourceIds []string) error {
return global.DB.Transaction(func(tx *gorm.DB) (err error) {
for i := range resourceIds {
resourceId := resourceIds[i]
id := utils.Sign([]string{resourceId, resourceType, userId})
resource := &Resource{
ID: id,
ResourceId: resourceId,
ResourceType: resourceType,
UserId: userId,
}
err = tx.Create(resource).Error
if err != nil {
return err
}
}
return nil
})
}

View File

@ -24,15 +24,14 @@ type User struct {
}
type UserVo struct {
ID string `gorm:"primary_key" json:"id"`
Username string `json:"username"`
Nickname string `json:"nickname"`
Online bool `json:"online"`
Enabled bool `json:"enabled"`
Created utils.JsonTime `json:"created"`
Type string `json:"type"`
//OwnerAssetCount int64 `json:"ownerAssetCount"`
SharerAssetCount int64 `json:"sharerAssetCount"`
ID string `gorm:"primary_key" json:"id"`
Username string `json:"username"`
Nickname string `json:"nickname"`
Online bool `json:"online"`
Enabled bool `json:"enabled"`
Created utils.JsonTime `json:"created"`
Type string `json:"type"`
SharerAssetCount int64 `json:"sharerAssetCount"`
}
func (r *User) TableName() string {
@ -51,7 +50,7 @@ func FindAllUser() (o []User) {
}
func FindPageUser(pageIndex, pageSize int, username, nickname string) (o []UserVo, total int64, err error) {
db := global.DB.Table("users").Select("users.id,users.username,users.nickname,users.online,users.enabled,users.created,users.type, count(resources.user_id) as sharer_asset_count").Joins("left join resources on users.id = resources.user_id and resources.resource_type = 'asset'").Group("users.id")
db := global.DB.Table("users").Select("users.id,users.username,users.nickname,users.online,users.enabled,users.created,users.type, count(resource_sharers.user_id) as sharer_asset_count").Joins("left join resource_sharers on users.id = resource_sharers.user_id and resource_sharers.resource_type = 'asset'").Group("users.id")
dbCounter := global.DB.Table("users")
if len(username) > 0 {
db = db.Where("users.username like ?", "%"+username+"%")