Java tutorial
/* * * Copyright (c) 2015 Caricah <info@caricah.com>. * * Caricah licenses this file to you under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. You may obtain a copy * of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. * * * * */ package com.caricah.iotracah.bootstrap.security.realm.impl; import com.caricah.iotracah.bootstrap.security.realm.IOTAbstractRealm; import com.caricah.iotracah.bootstrap.security.realm.auth.permission.IOTPermission; import com.caricah.iotracah.bootstrap.security.realm.state.IOTAccount; import com.caricah.iotracah.bootstrap.security.realm.state.IOTRole; import org.apache.shiro.authz.Permission; import org.apache.shiro.config.ConfigurationException; import org.apache.shiro.util.PermissionUtils; import org.apache.shiro.util.StringUtils; import java.text.ParseException; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * @author <a href="mailto:bwire@caricah.com"> Peter Bwire </a> * @version 1.0 10/6/15 */ public class IOTTextConfiguredRealm extends IOTAbstractRealm { private final Pattern usernamePartitionPattern = Pattern.compile("(?<username>.*)-<(?<partition>.*)>"); private final Pattern rolePartitionPattern = Pattern.compile("(?<rolename>.*)-<(?<partition>.*)>"); private volatile String userDefinitions; private volatile String roleDefinitions; private String defaultPartitionName; public String getDefaultPartitionName() { return defaultPartitionName; } public void setDefaultPartitionName(String defaultPartitionName) { this.defaultPartitionName = defaultPartitionName; } /** * Will call 'processDefinitions' on startup. * * @see <a href="https://issues.apache.org/jira/browse/SHIRO-223">SHIRO-223</a> * @since 1.2 */ @Override protected void onInit() { super.onInit(); processDefinitions(); } public String getUserDefinitions() { return userDefinitions; } /** * <p>Sets a newline (\n) delimited String that defines user-to-password-and-role(s) key/value pairs according * to the following format: * <p/> * <p><code><em>username</em> = <em>password</em>, role1, role2,...</code></p> * <p/> * <p>Here are some examples of what these lines might look like:</p> * <p/> * <p><code>root = <em>reallyHardToGuessPassword</em>, administrator<br/> * jsmith = <em>jsmithsPassword</em>, manager, engineer, employee<br/> * abrown = <em>abrownsPassword</em>, qa, employee<br/> * djones = <em>djonesPassword</em>, qa, contractor<br/> * guest = <em>guestPassword</em></code></p> * * @param userDefinitions the user definitions to be parsed and converted to Map.Entry elements */ public void setUserDefinitions(String userDefinitions) { this.userDefinitions = userDefinitions; } public String getRoleDefinitions() { return roleDefinitions; } /** * Sets a newline (\n) delimited String that defines role-to-permission definitions. * <p/> * <p>Each line within the string must define a role-to-permission(s) key/value mapping with the * equals character signifies the key/value separation, like so:</p> * <p/> * <p><code><em>rolename</em> = <em>permissionDefinition1</em>, <em>permissionDefinition2</em>, ...</code></p> * <p> * <p>where <em>permissionDefinition</em> is an arbitrary String, but must people will want to use * Strings that conform to the {@link org.apache.shiro.authz.permission.WildcardPermission WildcardPermission} * format for ease of use and flexibility. Note that if an individual <em>permissionDefnition</em> needs to * be internally comma-delimited (e.g. <code>printer:5thFloor:print,info</code>), you will need to surround that * definition with double quotes (") to avoid parsing errors (e.g. * <code>"printer:5thFloor:print,info"</code>). * <p> * <p><b>NOTE:</b> if you have roles that don't require permission associations, don't include them in this * definition - just defining the role name in the {@link #setUserDefinitions(String) userDefinitions} is * enough to create the role if it does not yet exist. This property is really only for configuring realms that * have one or more assigned Permission. * * @param roleDefinitions the role definitions to be parsed at initialization */ public void setRoleDefinitions(String roleDefinitions) { this.roleDefinitions = roleDefinitions; } protected void processDefinitions() { try { processRoleDefinitions(); processUserDefinitions(); } catch (ParseException e) { String msg = "Unable to parse user and/or role definitions."; throw new ConfigurationException(msg, e); } } protected void processRoleDefinitions() throws ParseException { String roleDefinitions = getRoleDefinitions(); if (roleDefinitions == null) { return; } Map<String, String> roleDefs = toMap(toLines(roleDefinitions)); processRoleDefinitions(roleDefs); } protected void processRoleDefinitions(Map<String, String> roleDefs) { if (roleDefs == null || roleDefs.isEmpty()) { return; } for (String rolename : roleDefs.keySet()) { String value = roleDefs.get(rolename); //Parse the rolename for partition. String partition; Matcher matcher = rolePartitionPattern.matcher(rolename); if (matcher.matches()) { partition = matcher.group("partition"); rolename = matcher.group("rolename"); } else { partition = getDefaultPartitionName(); } IOTRole role = getIOTRole(partition, rolename); if (role == null) { role = addIOTRole(partition, rolename); } Set<Permission> permissions = PermissionUtils.resolveDelimitedPermissions(value, getPermissionResolver()); for (Permission permission : permissions) { role.add((IOTPermission) permission); } saveIOTRole(role); } } protected void processUserDefinitions() throws ParseException { String userDefinitions = getUserDefinitions(); if (userDefinitions == null) { return; } Map<String, String> userDefs = toMap(toLines(userDefinitions)); processUserDefinitions(userDefs); } protected void processUserDefinitions(Map<String, String> userDefs) { if (userDefs == null || userDefs.isEmpty()) { return; } for (String username : userDefs.keySet()) { String value = userDefs.get(username); String[] passwordAndRolesArray = StringUtils.split(value); String password = passwordAndRolesArray[0]; String partition; Matcher matcher = usernamePartitionPattern.matcher(username); if (matcher.matches()) { partition = matcher.group("partition"); username = matcher.group("username"); } else { partition = getDefaultPartitionName(); } if (Objects.nonNull(username) && !username.isEmpty()) { IOTAccount account = getIOTAccount(partition, username); if (account == null) { account = addIOTAccount(partition, username, password); } account.setCredential(password); if (passwordAndRolesArray.length > 1) { for (int i = 1; i < passwordAndRolesArray.length; i++) { String rolename = passwordAndRolesArray[i]; account.addRole(rolename); } } saveIOTAccount(account); } } } protected static Set<String> toLines(String s) { LinkedHashSet<String> set = new LinkedHashSet<String>(); Scanner scanner = new Scanner(s); while (scanner.hasNextLine()) { set.add(scanner.nextLine()); } return set; } protected static Map<String, String> toMap(Collection<String> keyValuePairs) throws ParseException { if (keyValuePairs == null || keyValuePairs.isEmpty()) { return null; } Map<String, String> pairs = new HashMap<String, String>(); for (String pairString : keyValuePairs) { String[] pair = StringUtils.splitKeyValue(pairString); if (pair != null) { pairs.put(pair[0].trim(), pair[1].trim()); } } return pairs; } }