Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make rows inaccessible based on certain conditions #242

Closed
philiplb opened this issue Dec 11, 2020 · 2 comments
Closed

Make rows inaccessible based on certain conditions #242

philiplb opened this issue Dec 11, 2020 · 2 comments

Comments

@philiplb
Copy link

philiplb commented Dec 11, 2020

Hi,

I'm currently implementing a multi tenant feature. One thing I would like to do is to let the tenants only access rows they are supposed to access.
Currently, I'm successful with the List view with setting up a scope like this:

	foo.Scope(&admin.Scope{
		Name:    "Default Scope",
		Default: true,
		Handler: func(db *gorm.DB, context *qor.Context) *gorm.DB {
			if security.HasRole(context.Roles, security.RoleSuperAdmin) {
				return db
			}
			return db.Joins(<some joins>).Where(<some conditions>)
		},
	})

This works just fine on the list view like http://localhost:8080/admin/foo . But here is the catch: With the detail view, I can still access and even edit other rows! Just by guessing the ID at http://localhost:8080/admin/foo/<id>.

How would you make this inaccessible? I could do that in some BeforeUpdate functions directly in Gorm, but I would rather like to filter that out directly within qor/admin.

@th-lange
Copy link

This is an important feature!

@philiplb
Copy link
Author

I actually managed to do that with the roles:

func getAccessedResource(req *http.Request) (string, string) {

	re := regexp.MustCompile(`^/admin/tenants/(.*?)$`)
	match := re.FindStringSubmatch(req.RequestURI)
	if match != nil {
		return "tenant", match[1]
	}

	re = regexp.MustCompile(`^/admin/users/(.*?)$`)
	match = re.FindStringSubmatch(req.RequestURI)
	if match != nil {
		return "user", match[1]
	}

	return "", ""
}

func registerRoles() {
	roles.Register(roles.Anyone, func(req *http.Request, currentUser interface{}) bool {
		user, ok := currentUser.(*AdminUser)
		return ok && user != nil
	})
	roles.Register(security.RoleSuperAdmin, func(req *http.Request, currentUser interface{}) bool {
		user, ok := currentUser.(*AdminUser)
		if !ok || user == nil {
			return false
		}
		return security.HasRole(user.Roles, security.RoleSuperAdmin)
	})
	roles.Register(security.RoleAdmin, func(req *http.Request, currentUser interface{}) bool {
		user, ok := currentUser.(*AdminUser)
		if !ok || user == nil {
			return false
		}
		resource, id := getAccessedResource(req)
		return business.AccessResourceAllowed(user.Name, resource, id)
	})
}

In combination with

	tenant := target.AddResource(&model.Tenant{}, &admin.Config{
		IconName: "Workers",
		Permission: roles.
			Allow(roles.Create, security.RoleSuperAdmin).
			Allow(roles.Read, security.RoleAdmin).
			Allow(roles.Update, security.RoleAdmin).
			Allow(roles.Delete, security.RoleSuperAdmin),
	})
	tenant.IndexAttrs("-Admin")
	tenant.Meta(&admin.Meta{
		Name:       "Admin",
		Permission: roles.Allow(roles.CRUD, security.RoleSuperAdmin),
	})
	tenant.Scope(&admin.Scope{
		Name:    "Default Scope",
		Default: true,
		Handler: func(db *gorm.DB, context *qor.Context) *gorm.DB {
			if security.HasRole(context.Roles, security.RoleSuperAdmin) {
				return db
			}
			return db.Joins("JOIN adminaccount ON adminaccount.id = admin_id").Where("adminaccount.username = ?", context.CurrentUser.DisplayName())
		},
	})

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants