package osgi.authorization.useradmin.provider;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.NavigableMap;
import java.util.TreeMap;
import java.util.concurrent.Callable;
import org.osgi.dto.DTO;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.useradmin.Authorization;
import org.osgi.service.useradmin.Role;
import org.osgi.service.useradmin.User;
import org.osgi.service.useradmin.UserAdmin;
import org.slf4j.Logger;
import osgi.enroute.authorization.api.Authority;
import osgi.enroute.authorization.api.AuthorityAdmin;
import aQute.libg.glob.Glob;
@Component
public class AuthorityImpl implements Authority, AuthorityAdmin {
private static final Glob[] EMPTY_GLOBS = new Glob[0];
private Logger log;
private UserAdmin userAdmin;
private ThreadLocal<SecurityContext> context = new ThreadLocal<>();
static class SecurityContext extends DTO {
public String userId;
public Thread onThread;
public long started;
public NavigableMap<String,Assert> asserts = new TreeMap<>();
}
static class Assert {
final Glob[] matchers;
public Assert(String key) {
String[] parts = key.split(";");
matchers = parts.length == 0 ? EMPTY_GLOBS : new Glob[parts.length - 1];
for (int i = 1; i < parts.length; i++) {
matchers[i - 1] = new Glob(parts[i]);
}
}
public boolean matches(String[] arguments) {
if (matchers.length != arguments.length)
return false;
for (int i = 0; i < matchers.length; i++)
if (!matchers[i].matcher(arguments[i]).matches())
return false;
return true;
}
}
@Activate
void activate() {
}
@Override
public String getUserId() throws Exception {
SecurityContext context = this.context.get();
if (context == null)
return null;
return context.userId;
}
@Override
public List<String> getPermissions() throws Exception {
SecurityContext context = this.context.get();
if (context == null)
return null;
return new ArrayList<>(context.asserts.keySet());
}
@Override
public boolean hasPermission(String permission, String... arguments) throws Exception {
SecurityContext context = this.context.get();
if (context == null)
return false;
NavigableMap<String,Assert> tailMap = context.asserts.tailMap(permission, true);
for (Map.Entry<String,Assert> entry : tailMap.entrySet()) {
String key = entry.getKey();
if (!key.startsWith(permission))
break;
if (permission.length() < key.length() && key.charAt(permission.length()) != ';')
break;
Assert a = entry.getValue();
if (a == null) {
a = new Assert(key);
entry.setValue(a);
}
if (a.matches(arguments))
return true;
}
return false;
}
@Override
public void checkPermission(String permission, String... arguments) throws Exception {
if (hasPermission(permission, arguments))
return;
log.warn("User %s fails to get permission %s with args %s", permission, arguments);
if (arguments.length == 0)
throw new SecurityException("User " + getUserId() + " does not have permission " + permission);
throw new SecurityException("User " + getUserId() + " does not have permission " + permission
+ " for arguments " + Arrays.toString(arguments));
}
@Override
public <T> T call(String userId, Callable<T> task) throws Exception {
SecurityContext context = new SecurityContext();
try {
if (userId == null)
userId = Role.USER_ANYONE;
Role role = userAdmin.getRole(userId);
if (role == null)
throw new IllegalArgumentException("No such user " + userId);
if (!(role instanceof User))
throw new IllegalArgumentException("This id is not for a User " + userId);
User user = (User) role;
context.userId = user.getName();
context.onThread = Thread.currentThread();
context.started = System.currentTimeMillis();
Authorization authorization = userAdmin.getAuthorization(user);
String[] roleNames = authorization.getRoles();
if (roleNames != null) {
for (String roleName : roleNames) {
context.asserts.put(roleName, null);
}
}
this.context.set(context);
return task.call();
}
catch (Exception e) {
log.error("Invocation for user " + userId + " failed", e);
throw e;
}
finally {
this.context.set(null);
}
}
@Reference
void setUserAdmin(UserAdmin userAdmin) {
this.userAdmin = userAdmin;
}
@Reference
void setLog(Logger logger) {
this.log = logger;
}
}