incodehq / incode-platform

Combines incode.org modules and isisaddons.org into a single set of modules.
http://platform.incode.org
Apache License 2.0
8 stars 9 forks source link

spi-security: Use SudoService.Spi to propagate user/role permissions to an effective user #45

Open danhaywood opened 6 years ago

danhaywood commented 6 years ago

from https://github.com/isisaddons-legacy/isis-module-security/issues/48

As introduced in 1.13.2

I tried to implement this at the generic Shiro security mechanism (within Isis core), but realized that actually it needs to be at a deeper level, ie in this module.

The code I sketched out before abandoning was:

@DomainService(nature = NatureOfService.DOMAIN)
public class SudoServiceSpi implements SudoService.Spi {

    @Override
    public void runAs(final String username, final List<String> roles) {
        try {
            final Subject subject = SecurityUtils.getSubject();
            if (subject == null) {
                return;
            }
            // can't set runAs if has no current principals
            if(!hasPrincipals(subject)) {
                return;
            }
            final SimplePrincipalCollection principals = new SimplePrincipalCollection();
            principals.add(new RunAsPrincipal(username, roles), "sudoRealm");
            subject.runAs(principals);
        } catch(UnavailableSecurityManagerException ex) {
            return;
        }
    }

    private boolean hasPrincipals(final Subject subject) {
        return !CollectionUtils.isEmpty(subject.getPrincipals());
    }

    @Override
    public void releaseRunAs() {
        try {
            final Subject subject = SecurityUtils.getSubject();
            if(subject == null) {
                return;
            }
            // can't set runAs if has no current principals
            if(!hasPrincipals(subject) || !subject.isRunAs()) {
                return;
            }
            subject.releaseRunAs();
        } catch (UnavailableSecurityManagerException ex) {
            return;
        }
    }
}

and also:

public class RunAsPrincipal implements AuthorizationInfo {

    private final String username;
    private final List<String> roles;

    public RunAsPrincipal(final String username, final List<String> roles) {
        this.username = username;
        this.roles = roles;
    }

    @Override
    public Collection<String> getRoles() {
        return roles;
    }

    @Override
    public Collection<String> getStringPermissions() {
        return Collections.emptyList();
    }

    @Override
    public Collection<Permission> getObjectPermissions() {
        return Collections.emptyList();
    }
}

Certainly RunAsPrincipal won't be needed; instead use the PrincipalForApplicationUser that already exists.

Also, will need to decide what to do if the specified user doesn't exist in the user; perhaps fall back on the above generic code (meaning no permissions, of course).