Skip to content

Commit

Permalink
Proof of concept support for authorization.
Browse files Browse the repository at this point in the history
Indirectly related to #16.
  • Loading branch information
splatch committed Feb 3, 2021
1 parent 553dc1d commit 4a5d662
Show file tree
Hide file tree
Showing 29 changed files with 786 additions and 27 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/**
* Copyright (c) 2019-2020 Contributors to the OpenSmartHouse project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.core.internal.auth;

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import org.openhab.core.auth.Authentication;
import org.openhab.core.auth.AuthorizationManager;
import org.openhab.core.auth.Permission;
import org.openhab.core.auth.PermissionEvaluator;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.component.annotations.ReferenceCardinality;
import org.osgi.service.component.annotations.ReferencePolicy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Component
public class AuthorizationManagerImpl implements AuthorizationManager {

private final Logger logger = LoggerFactory.getLogger(AuthorizationManagerImpl.class);
private List<PermissionEvaluator<Object>> evaluators = new CopyOnWriteArrayList<>();

@Override
public <T> boolean hasPermission(Permission permission, T object, Authentication authentication) {
if (authentication == null) {
return false;
}

for (PermissionEvaluator<Object> evaluator : evaluators) {
if (evaluator.supports(object.getClass(), permission)) {
if (!evaluator.hasPermission(permission, authentication, object)) {
logger.trace("Access denied to object {} ({}) for authentication {} reported by evaluator {}",
object, object.getClass().getName(), authentication, evaluator);
return false;
}
}
}
return true;
}

public boolean hasPermission(Permission permission, String id, Class<?> type, Authentication authentication) {
if (authentication == null) {
return false;
}

for (PermissionEvaluator<Object> evaluator : evaluators) {
if (evaluator.supports(type, permission)) {
if (!evaluator.hasPermission(permission, authentication, (Class<Object>) type, id)) {
logger.trace("Access denied to object with id {} ({}) for authentication {} reported by evaluator {}",
id, type.getName(), authentication, evaluator);
return false;
}
}
}
return true;
}

@Reference(cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC)
public void addPermissionEvaluator(PermissionEvaluator<Object> evaluator) {
this.evaluators.add(evaluator);
}

public void removePermissionEvaluator(PermissionEvaluator<Object> evaluator) {
this.evaluators.remove(evaluator);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
*/
package org.openhab.core.auth.local;

import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

Expand All @@ -28,16 +29,23 @@ public class GenericUser implements User {

private String name;
private Set<String> roles;
private Set<String> permissions;

/**
* Constructs a user attributed with a set of roles.
*
* @param name the username (account name)
* @param roles the roles attributed to this user
* @param permissions the items user has access to
*/
public GenericUser(String name, Set<String> roles) {
public GenericUser(String name, Set<String> roles, Set<String> permissions) {
this.name = name;
this.roles = roles;
this.permissions = permissions;
}

public GenericUser(String name, Set<String> roles) {
this(name, roles, Collections.emptySet());
}

/**
Expand All @@ -64,7 +72,12 @@ public Set<String> getRoles() {
return roles;
}

@Override
public Set<String> getPermissions() {
return permissions;
}

public String toString() {
return name + " (" + roles + ")";
return name + " (" + roles + " " + permissions + ")";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.auth.local.User;

/**
* A {@link User} sourced from a managed {@link UserProvider}.
Expand All @@ -33,6 +32,7 @@ public class ManagedUser implements User {
private String passwordHash;
private String passwordSalt;
private Set<String> roles = new HashSet<>();
private Set<String> permissions = new HashSet<>();
private @Nullable PendingToken pendingToken = null;
private List<UserSession> sessions = new ArrayList<>();
private List<UserApiToken> apiTokens = new ArrayList<>();
Expand Down Expand Up @@ -120,6 +120,14 @@ public void setRoles(Set<String> roles) {
this.roles = roles;
}

public void setPermissions(Set<String> permissions) {
this.permissions = permissions;
}

public Set<String> getPermissions() {
return permissions;
}

/**
* Gets the pending token information for this user, if any.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,12 @@ public interface User extends Principal, Identifiable<String> {
* @see Role
* @return role attributed to the user
*/
public Set<String> getRoles();
Set<String> getRoles();

/**
* Returns items which given user have access to.
*
* @return Item identifiers user has access to.
*/
Set<String> getPermissions();
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public class Authentication {
private String username;
private Set<String> roles;
private String scope;
private Set<String> items;
private Set<String> permissions;

/**
* no-args constructor required by gson
Expand All @@ -38,7 +38,7 @@ protected Authentication() {
this.username = null;
this.roles = null;
this.scope = null;
this.items = null;
this.permissions = null;
}

/**
Expand All @@ -59,7 +59,7 @@ public Authentication(String username, String... roles) {
* @param scope a scope this authentication is valid for
*/
public Authentication(String username, String[] roles, String scope) {
this(username, roles, scope, new String[] {"*"});
this(username, roles, scope, new String[] {"*:*:*"});
}

/**
Expand All @@ -68,12 +68,13 @@ public Authentication(String username, String[] roles, String scope) {
* @param username name of the user associated to this authentication instance
* @param roles a variable list of roles that the user possesses.
* @param scope a scope this authentication is valid for
* @param permissions permissions associated with authentication
*/
public Authentication(String username, String[] roles, String scope, String[] items) {
public Authentication(String username, String[] roles, String scope, String[] permissions) {
this.username = username;
this.roles = Set.of(roles);
this.scope = scope;
this.items = Set.of(items);
this.permissions = Set.of(permissions);
}

/**
Expand Down Expand Up @@ -104,11 +105,11 @@ public String getScope() {
}

/**
* Retrieves the items this authentication is valid for.
* Retrieves the permissions this authentication has.
*
* @return an set of items (might be empty)
* @return an set of permissions (might be empty)
*/
public Set<String> getItems() {
return items;
public Set<String> getPermissions() {
return permissions;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/**
* Copyright (c) 2019-2020 Contributors to the OpenSmartHouse project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.core.auth;

import java.util.Optional;
import org.eclipse.jdt.annotation.Nullable;

/**
* The access layer to an authentication context during execution of an action.
*
* @author Łukasz Dywicki - Initial contribution.
*/
public interface AuthenticationContextHolder {

@Nullable
Authentication getAuthentication();

Optional<Authentication> fetchAuthentication();

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/**
* Copyright (c) 2019-2020 Contributors to the OpenSmartHouse project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.core.auth;

/**
* The access layer to an authentication context during execution of an action.
*
* @author Łukasz Dywicki - Initial contribution.
*/
public class AuthorizationException extends Exception {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.core.auth;

/**
* Authorization manager is main entry point for security checks.
*
* @author Łukasz Dywicki - Initial contribution
*/
public interface AuthorizationManager {

<T> boolean hasPermission(Permission permission, T object, Authentication authentication);

boolean hasPermission(Permission permission, String id, Class<?> type, Authentication authentication);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/**
* Copyright (c) 2019-2020 Contributors to the OpenSmartHouse project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.core.auth;

import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.stream.Collectors;

/**
* Helpers for filtering out collections and returning trimmed data.
*
* @author Łukasz Dywicki - Initial contribution
*/
public class AuthorizationManagerFilters {

public static <T> List<T> filter(Authentication authentication, BiFunction<Authentication, T, Boolean> check, List<T> values) {
return values.stream().filter(value -> check.apply(authentication, value))
.collect(Collectors.toList());
}

public static <T> Set<T> filter(Authentication authentication, BiFunction<Authentication, T, Boolean> check, Set<T> values) {
return values.stream().filter(value -> check.apply(authentication, value))
.collect(Collectors.toSet());
}

public static <K, V> Map<K, V> filter(Authentication authentication, BiFunction<Authentication, Entry<K, V>, Boolean> check, Map<K, V> values) {
return values.entrySet().stream().filter(entry -> check.apply(authentication, entry))
.collect(Collectors.toMap(Entry::getKey, Entry::getValue));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/**
* Copyright (c) 2019-2020 Contributors to the OpenSmartHouse project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.core.auth;

/**
* Writeable holder for authentication context which should be used early in processing chain.
*
* @author Łukasz Dywicki - Initial contribution.
*/
public interface MutableAuthenticationContextHolder {

void setAuthentication(Authentication authentication);

}
Loading

0 comments on commit 4a5d662

Please sign in to comment.