Skip to content

Commit

Permalink
- fix attribute assigment
Browse files Browse the repository at this point in the history
- support subgroups and composite roles
  • Loading branch information
vzakharchenko committed Jan 12, 2020
1 parent 71ff5bd commit 1195219
Show file tree
Hide file tree
Showing 34 changed files with 370 additions and 312 deletions.
22 changes: 18 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
# keycloak-radius-plugin

[![CircleCI](https://circleci.com/gh/vzakharchenko/keycloak-radius-plugin/tree/master.svg?style=svg)](https://circleci.com/gh/vzakharchenko/keycloak-radius-plugin/tree/master) [![Total alerts](https://img.shields.io/lgtm/alerts/g/vzakharchenko/keycloak-radius-plugin.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/vzakharchenko/keycloak-radius-plugin/alerts/)
[![Total alerts](https://img.shields.io/lgtm/alerts/g/vzakharchenko/keycloak-radius-plugin.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/vzakharchenko/keycloak-radius-plugin/alerts/) [![Coverage Status](https://coveralls.io/repos/github/vzakharchenko/keycloak-radius-plugin/badge.svg?branch=master)](https://coveralls.io/github/vzakharchenko/keycloak-radius-plugin?branch=master) <a href="https://codeclimate.com/github/vzakharchenko/keycloak-radius-plugin/maintainability"><img src="https://api.codeclimate.com/v1/badges/499d56ae9242cfaf2cbb/maintainability" /></a>
The plugin is developed and works with Mikrotik RouterOS
[![CircleCI](https://circleci.com/gh/vzakharchenko/keycloak-radius-plugin/tree/master.svg?style=svg)](https://circleci.com/gh/vzakharchenko/keycloak-radius-plugin/tree/master)
[![Language grade: Java](https://img.shields.io/lgtm/grade/java/g/vzakharchenko/keycloak-radius-plugin.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/vzakharchenko/keycloak-radius-plugin/context:java)
[![Coverage Status](https://coveralls.io/repos/github/vzakharchenko/keycloak-radius-plugin/badge.svg?branch=master)](https://coveralls.io/github/vzakharchenko/keycloak-radius-plugin?branch=master)
<a href="https://codeclimate.com/github/vzakharchenko/keycloak-radius-plugin/maintainability"><img src="https://api.codeclimate.com/v1/badges/499d56ae9242cfaf2cbb/maintainability" /></a>

features:

Expand All @@ -11,9 +12,10 @@ features:
- openID connect
- login using facebook, google, etc...
- login
- support api, winbox, web
- support api, winbox, web(Mikrotik)

## Configuration
### Radius server config file
- create file \<Keycloak\>/config/radius.config
- example <pre><code>{
"provider" : "radius-provider",
Expand All @@ -27,3 +29,15 @@ features:
"useRadius" : true
}</code></pre>

### Keycloak Client with Radius Protocol
![radiusProtocol](docs/radiusProtocol.png)

### Assign Radius Attributes to Role
> **_NOTE:_** Composite roles supported
![RoleAttributes](docs/RoleAttributes.png)
### Assign Radius Attributes to Group
> **_NOTE:_** SubGroups supported
![groupAttributes](docs/groupAttributes.png)
### Assign Radius Attributes to User
![userAttributes](docs/userAttributes.png)
Binary file added docs/RoleAttributes.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/groupAttributes.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/radiusProtocol.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/userAttributes.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 0 additions & 13 deletions keycloak-plugins/radius-plugin/git.properties

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,19 @@
import ua.zaskarius.keycloak.plugins.radius.configuration.RadiusConfigHelper;
import ua.zaskarius.keycloak.plugins.radius.models.RadiusServerSettings;
import ua.zaskarius.keycloak.plugins.radius.password.RadiusCredentialModel;
import ua.zaskarius.keycloak.plugins.radius.providers.IRadiusDictionaryProvider;
import ua.zaskarius.keycloak.plugins.radius.providers.IRadiusServerProvider;

import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

public final class RadiusHelper {

private static List<String> realmAttributes = new ArrayList<>();

private RadiusHelper() {
}

Expand Down Expand Up @@ -66,9 +71,29 @@ public static IRadiusServerProvider getProvider(KeycloakSession session) {
radiusSettings.getProvider());
}

public static List<String> getRealmAttributes(KeycloakSession session) {
if (realmAttributes.isEmpty()) {
Set<IRadiusDictionaryProvider> providers = session
.getAllProviders(IRadiusDictionaryProvider.class);
for (IRadiusDictionaryProvider provider : providers) {
List<String> attributes = provider.getRealmAttributes();
if (attributes != null) {
realmAttributes.addAll(attributes);
}
}
}

return realmAttributes;
}


public static boolean isUseRadius(KeycloakSession session) {
IRadiusConfiguration config = RadiusConfigHelper.getConfig();
return config.getRadiusSettings(session).isUseRadius();
}


public static void setRealmAttributes(List<String> realmAttributes) {
RadiusHelper.realmAttributes = realmAttributes;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,26 @@
import ua.zaskarius.keycloak.plugins.radius.radius.handlers.attributes.KeycloakAttributesType;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;

public class RadiusAttributeHolder<T> {
private KeycloakAttributesType type;
private Map<String, List<String>> attributes = new HashMap<>();
private Map<String, Set<String>> attributes = new HashMap<>();
private T object;

public RadiusAttributeHolder(KeycloakAttributesType type, T object) {
this.type = type;
this.object = object;
}

public void addAttribute(String name, List<String> value) {
public void addAttribute(String name, Set<String> value) {
attributes.put(name, value);
}

public Map<String, List<String>> getAttributes() {
public Map<String, Set<String>> getAttributes() {
return attributes;
}

Expand All @@ -34,7 +34,7 @@ public T getObject() {
return object;
}

public void filter(Predicate<Map.Entry<String, List<String>>> predicate) {
public void filter(Predicate<Map.Entry<String, Set<String>>> predicate) {
this.attributes = attributes.entrySet()
.stream().filter(predicate)
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@
public interface IRadiusDictionaryProvider extends Provider {
DictionaryParser getDictionaryParser();
List<String> getResources();
List<String> getRealmAttributes();
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,9 @@ public DictionaryParser getDictionaryParser() {
public List<String> getResources() {
return Collections.singletonList("org/tinyradius/dictionary/default_dictionary");
}

@Override
public List<String> getRealmAttributes() {
return null;
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,9 @@ public DictionaryParser getDictionaryParser() {
public List<String> getResources() {
return Collections.singletonList("MS");
}

@Override
public List<String> getRealmAttributes() {
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,9 @@ public DictionaryParser getDictionaryParser() {
public List<String> getResources() {
return Collections.singletonList("mikrotik");
}

@Override
public List<String> getRealmAttributes() {
return Collections.singletonList("Mikrotik-Realm");
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
package ua.zaskarius.keycloak.plugins.radius.radius.handlers.attributes;

import ua.zaskarius.keycloak.plugins.radius.event.log.EventLoggerFactory;
import ua.zaskarius.keycloak.plugins.radius.models.RadiusAttributeHolder;
import ua.zaskarius.keycloak.plugins.radius.models.RadiusUserInfo;
import ua.zaskarius.keycloak.plugins.radius.radius.handlers.attributes.conditionals.AttributeConditional;
import ua.zaskarius.keycloak.plugins.radius.radius.handlers.session.KeycloakSessionUtils;
import org.jboss.logging.Logger;
import org.keycloak.events.EventBuilder;
import org.keycloak.events.EventType;
Expand All @@ -13,6 +8,11 @@
import org.tinyradius.dictionary.Dictionary;
import org.tinyradius.packet.AccessRequest;
import org.tinyradius.packet.RadiusPacket;
import ua.zaskarius.keycloak.plugins.radius.event.log.EventLoggerFactory;
import ua.zaskarius.keycloak.plugins.radius.models.RadiusAttributeHolder;
import ua.zaskarius.keycloak.plugins.radius.models.RadiusUserInfo;
import ua.zaskarius.keycloak.plugins.radius.radius.handlers.attributes.conditionals.AttributeConditional;
import ua.zaskarius.keycloak.plugins.radius.radius.handlers.session.KeycloakSessionUtils;

import java.util.ArrayList;
import java.util.List;
Expand All @@ -22,7 +22,6 @@

public abstract class AbstractKeycloakAttributes<KEYCLOAK_TYPE> implements KeycloakAttributes {

public static final String RADIUS_ATTRIBUTES = "Radius Attributes";
private static final Logger LOGGER = Logger.getLogger(AbstractKeycloakAttributes.class);
protected final KeycloakSession session;
protected final RadiusUserInfo radiusUserInfo;
Expand All @@ -38,20 +37,18 @@ public AbstractKeycloakAttributes(KeycloakSession session) {

protected abstract Set<KEYCLOAK_TYPE> getKeycloakTypes();

protected abstract List<String> getAttributes(KEYCLOAK_TYPE type,
String attributeName);
protected abstract Map<String, Set<String>> getAttributes(KEYCLOAK_TYPE type);

@Override
public KeycloakAttributes read() {
Set<KEYCLOAK_TYPE> groups = getKeycloakTypes();
for (KEYCLOAK_TYPE type : groups) {
List<String> radiusAttributes = getAttributes(type, RADIUS_ATTRIBUTES);
if (radiusAttributes != null) {
for (String radiusAttribute : radiusAttributes) {
Map<String, Set<String>> attributes = getAttributes(type);
if (attributes != null) {
for (Map.Entry<String, Set<String>> entry : attributes.entrySet()) {
RadiusAttributeHolder<KEYCLOAK_TYPE> attributeHolder =
new RadiusAttributeHolder<>(getType(), type);
attributeHolder.addAttribute(radiusAttribute,
getAttributes(type, radiusAttribute));
attributeHolder.addAttribute(entry.getKey(), entry.getValue());
attributeHolders.add(attributeHolder);
}
}
Expand Down Expand Up @@ -115,8 +112,8 @@ public KeycloakAttributes filter(AccessRequest accessRequest) {
@Override
public void fillAnswer(RadiusPacket answer) {
for (RadiusAttributeHolder<KEYCLOAK_TYPE> attributeHolder : attributeHolders) {
Map<String, List<String>> attributes = attributeHolder.getAttributes();
for (Map.Entry<String, List<String>> entry : attributes.entrySet()) {
Map<String, Set<String>> attributes = attributeHolder.getAttributes();
for (Map.Entry<String, Set<String>> entry : attributes.entrySet()) {
String attributeName = entry.getKey();
for (String value : entry.getValue()) {
answer.addAttribute(attributeName, value);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package ua.zaskarius.keycloak.plugins.radius.radius.handlers.attributes;

import org.keycloak.models.GroupModel;
import org.keycloak.models.RoleModel;

import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

public final class AttributeWalkerUtils {

private AttributeWalkerUtils() {
}

public static void groupWalker(GroupModel groupModel, Map<String, Set<String>> attributes) {
Set<GroupModel> subGroups = groupModel.getSubGroups();
if (subGroups != null) {
for (GroupModel subGroup : subGroups) {
groupWalker(subGroup, attributes);
}
}
Set<RoleModel> roleMappings = groupModel.getRoleMappings();
if (roleMappings != null) {
for (RoleModel roleModel : roleMappings) {
roleWalker(roleModel, attributes);
}
}
Map<String, List<String>> modelAttributes = groupModel.getAttributes();
mergeMaps(modelAttributes, attributes);
}


private static void mergeMaps(
Map<String, List<String>> modelAttributes1,
Map<String, Set<String>> modelAttributes2
) {
modelAttributes1.forEach((s, strings) -> {
Set<String> set = modelAttributes2.get(s);
if (set == null) {
set = new HashSet<>();
modelAttributes2.put(s, set);
}
if (strings != null && !strings.isEmpty()) {
set.addAll(strings);
}
});
}

public static void roleWalker(RoleModel roleModel, Map<String, Set<String>> attributes) {
if (roleModel.isComposite()) {
Set<RoleModel> composites = roleModel.getComposites();
for (RoleModel r : composites) {
roleWalker(r, attributes);
}
}
mergeMaps(roleModel.getAttributes(), attributes);
}
}
Loading

0 comments on commit 1195219

Please sign in to comment.