/*
* ====================
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved.
*
* The contents of this file are subject to the terms of the Common Development
* and Distribution License("CDDL") (the "License"). You may not use this file
* except in compliance with the License.
*
* You can obtain a copy of the License at
* http://opensource.org/licenses/cddl1.php
* See the License for the specific language governing permissions and limitations
* under the License.
*
* When distributing the Covered Code, include this CDDL Header Notice in each file
* and include the License file at http://opensource.org/licenses/cddl1.php.
* If applicable, add the following below this CDDL Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyrighted [year] [name of copyright owner]"
* ====================
*/
package org.identityconnectors.solaris.operation.nis;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.identityconnectors.common.StringUtil;
import org.identityconnectors.common.security.GuardedString;
import org.identityconnectors.framework.common.exceptions.ConnectorException;
import org.identityconnectors.framework.common.objects.Attribute;
import org.identityconnectors.framework.common.objects.OperationalAttributes;
import org.identityconnectors.solaris.SolarisConnection;
import org.identityconnectors.solaris.attr.NativeAttribute;
import org.identityconnectors.solaris.operation.search.SolarisEntry;
public class AbstractNISOp {
public final static String WHO_I_AM = "WHOIAM=`who am i | cut -d ' ' -f1`";
// Temporary file names
final static String TMP_PWDFILE_1 = "/tmp/wspasswd.$$";
final static String TMP_PWDFILE_2 = "/tmp/wspasswd_work.$$";
final static String TMP_PWDFILE_3 = "/tmp/wspasswd_out.$$";
final static String PWD_MUTEX_FILE = "/tmp/WSpwdlock";
final static String TMP_PWD_MUTEX_FILE = "/tmp/WSpwdlock.$$";
final static String PWD_PID_FILE = "/tmp/WSpwdpid.$$";
// GROUP constants
final static String DUPLICATE_GROUP_NAME_MSG = "Duplicate group name";
final static String DUPLICATE_GROUP_ID_MSG = "Duplicate group id";
// GROUP "Mutex" files
protected final static String GRP_MUTEX_FILE = "/tmp/WSgrplock";
protected final static String TMP_GRP_MUTEX_FILE = "/tmp/WSgrplock.$$";
protected final static String GRP_PID_FILE = "/tmp/WSgrppid.$$";
// This is a major string to look for if you want to do rejects on shadow
// file errors
final static String ERROR_MODIFYING = "Error modifying ";
final static Set<NativeAttribute> ALLOWED_NIS_ATTRIBUTES;
static {
ALLOWED_NIS_ATTRIBUTES = new HashSet<NativeAttribute>();
ALLOWED_NIS_ATTRIBUTES.add(NativeAttribute.ID);
ALLOWED_NIS_ATTRIBUTES.add(NativeAttribute.GROUP_PRIM);
ALLOWED_NIS_ATTRIBUTES.add(NativeAttribute.DIR);
ALLOWED_NIS_ATTRIBUTES.add(NativeAttribute.COMMENT);
ALLOWED_NIS_ATTRIBUTES.add(NativeAttribute.SHELL);
}
protected final static GuardedString getPassword(SolarisEntry entry) {
GuardedString password = null;
for (Attribute passAttr : entry.getAttributeSet()) {
if (passAttr.getName().equals(OperationalAttributes.PASSWORD_NAME)) {
password = (GuardedString) passAttr.getValue().get(0);
break;
}
}
return password;
}
public static void addNISMake(String target, SolarisConnection conn) {
final String makeCmd = conn.buildCommand(true, "/usr/ccs/bin/make");
final String nisDir = conn.getConfiguration().getNisBuildDirectory();
StringBuilder buildscript = new StringBuilder("nisdomain=`domainname`; ");
buildscript.append("cd " + nisDir + "/$nisdomain\n");
buildscript.append(makeCmd + "-f ../Makefile " + target + "; cd");
try {
conn.executeCommand(buildscript.toString());
conn.waitForRootShellPrompt();
} catch (Exception ex) {
throw ConnectorException.wrap(ex);
}
}
public static String getRemovePwdTmpFiles(SolarisConnection conn) {
final String rmCmd = conn.buildCommand(false, "rm");
// @formatter:off
String removePwdTmpFiles =
"if [ -f " + TMP_PWDFILE_1 + " ]; then " +
rmCmd + " -f " + TMP_PWDFILE_1 + "; " +
"fi; " +
"if [ -f " + TMP_PWDFILE_2 + " ]; then " +
rmCmd + " -f " + TMP_PWDFILE_2 + "; " +
"fi; " +
"if [ -f " + TMP_PWDFILE_3 + " ]; then " +
rmCmd + " -f " + TMP_PWDFILE_3 + "; " +
"fi";
// @formatter:on
return removePwdTmpFiles;
}
/**
* filters the given entry's attributes, so they are just the ones that are
* allowed NIS attributes.
*/
public static Map<NativeAttribute, List<Object>> constructNISUserAttributeParameters(
SolarisEntry entry, Set<NativeAttribute> allowedNISattributes) {
Map<NativeAttribute, List<Object>> result = new HashMap<NativeAttribute, List<Object>>();
for (Attribute attr : entry.getAttributeSet()) {
String type = attr.getName();
List<Object> value = attr.getValue();
for (NativeAttribute nattr : allowedNISattributes) {
if (type.equals(nattr.toString())) {
result.put(NativeAttribute.forAttributeName(type), value);
break;
}
}
}
return result;
}
/**
* check if the users exists, that are given by the user list argument.
*
* @param group
* name of the new group.
* @param userNames
* The list of usernames that we want to add to the new group.
* @param conn
* <p>
* Note: used in both native and NIS attributes. <br/>
* <br/>
* Add commands to the script that will check to see which users
* exist. Only attempt to add the user that exist to the group.
* Generate and echo a list of users that do not exist, so that a
* message can be displayed indicating this.
* <p>
* Note 2: Any checks on users need to be done prior to invoking
* this method.
*/
public static void changeGroupMembers(String group, List<Object> userNames, boolean isNIS,
SolarisConnection conn) {
// Three temporary files are needed by this script.
// One to serve as a baseline to make sure /etc/group doesn't
// change underneath us. The second is to serve as the destination
// for the changes to be copied to /etc/group. The third is as
// an intermediate step so that sudo could function correctly.
final String tmpfile1 = "/tmp/wsgroup.$$";
final String tmpfile2 = "/tmp/wsgroupwork.$$";
final String tmpfile3 = "/tmp/wsgroupwork2.$$";
final String cpCmd = conn.buildCommand(false, "/usr/bin/cp");
final String rmCmd = conn.buildCommand(false, "rm");
final String mvCmd = conn.buildCommand(false, "mv");
final String grepCmd = conn.buildCommand(false, "grep");
final String sedCmd = conn.buildCommand(false, "sed");
final String diffCmd = conn.buildCommand(false, "diff");
final String chownCmd = conn.buildCommand(false, "chown");
final String realUserCmd;
final String groupFile;
if (!isNIS) {
groupFile = "/etc/group";
realUserCmd =
conn.buildCommand(true, "logins", "-ol $WSUSER 2>&1 | grep \"not found\"");
} else {
String pwdDir = conn.getConfiguration().getNisPwdDir();
groupFile = pwdDir + "/group";
realUserCmd = "ypmatch $WSUSER passwd 2>&1 | grep \"an't match key\"";
}
// @formatter:off
final String checkUsers =
"for WSUSER in $WSUSERS;\n" +
"do " +
"USERTEXT=`" + realUserCmd + "`;\n" +
"if [ -n \"$USERTEXT\" ]; then\n" +
"if [ -z \"$BADWSUSERS\" ]; then\n" +
"BADWSUSERS=\"$WSUSER\";\n" +
"else " +
"BADWSUSERS=\"$BADWSUSERS,$WSUSER\";\n" +
"fi\n" +
"else " +
"if [ -z \"$ADDWSUSERS\" ]; then\n" +
"ADDWSUSERS=\"$WSUSER\";\n" +
"else "+
"ADDWSUSERS=\"$ADDWSUSERS,$WSUSER\";\n" +
"fi\n" +
"fi\n" +
"done";
final String rmTmpFiles =
"if [ -f " + tmpfile1 + " ]; then " +
rmCmd + " -f " + tmpfile1 + "; " +
"fi; " +
"if [ -f " + tmpfile2 + " ]; then " +
rmCmd + " -f " + tmpfile2 + "; " +
"fi; " +
"if [ -f " + tmpfile3 + " ]; then " +
rmCmd + " -f " + tmpfile3 + "; " +
"fi";
final String changeUsersSetup =
cpCmd + " -p " + groupFile + " " + tmpfile1 + "; " +
cpCmd + " -p " + groupFile + " " + tmpfile2;
final String changeUsersEnv =
"OWNER=`ls -l " + groupFile + " | awk '{ print $3 }'`; " +
"GOWNER=`ls -l " + groupFile + " | awk '{ print $4 }'`; " +
"GROUPTEXT=`" + grepCmd + " \"^$WSTARGETGRP:\" " + tmpfile1 + "`";
final String changeUsers =
"if [ -n \"$GROUPTEXT\" ]; then\n" +
"GRPPWD=`echo $GROUPTEXT | awk -F: '{ print $2 }'`; " +
"GRPGID=`echo $GROUPTEXT | awk -F: '{ print $3 }'`; " +
"REPLTEXT=`echo $WSTARGETGRP:$GRPPWD:$GRPGID:`;\n" +
sedCmd + " 's/'$GROUPTEXT'/'$REPLTEXT''$ADDWSUSERS'/g' " + tmpfile1 + " > " + tmpfile3 + "; \n" +
diffCmd + " " + groupFile + " " + tmpfile1 + " 2>/dev/null 1>/dev/null; " +
"RC=$?;\n" +
"if [ $RC -eq 0 ]; then " +
cpCmd + " -f " + tmpfile3 + " " + tmpfile2 + "; " +
mvCmd + " -f " + tmpfile2 + " " + groupFile + ";\n" +
chownCmd + " $OWNER:$GOWNER " + groupFile + "; " +
"else " +
"GRPERRMSG=\"Error modifying " + groupFile + ", edit $WSTARGETGRP to include correct groups.\"; \n" +
"fi \n" +
"else " +
"GRPERRMSG=\"$WSTARGETGRP not found in " + groupFile + ".\"; " +
"fi";
// @formatter:on
// Clear the environment variables that will be used. The connection to
// the resource is pooled.
conn.executeCommand("BADWSUSERS=;ADDWSUSERS=;GRPERRMSG=");
// for loop in bash script needs symbols separated by blank space.
final String userList = prepareUserList(userNames);
conn.executeCommand(String.format("WSUSERS=\"%s\"", userList));
conn.executeCommand(checkUsers);
final String badUsers = conn.executeCommand("echo $BADWSUSERS");
if (StringUtil.isNotBlank(badUsers)) {
throw new ConnectorException("SolarisConnector: users do not exist: " + badUsers);
}
conn.executeCommand("WSTARGETGRP='" + group + "'");
conn.executeCommand(rmTmpFiles);
conn.executeCommand(changeUsersSetup);
conn.executeCommand(changeUsersEnv);
conn.executeCommand(changeUsers);
conn.executeCommand(rmTmpFiles);
final String grpErrMsg = conn.executeCommand("echo $GRPERRMSG");
if (StringUtil.isNotBlank(grpErrMsg)) {
throw new ConnectorException(grpErrMsg);
}
// Reset the environment variables in case this connection
// is reused for this task
conn.executeCommand("unset BADWSUSERS;unset ADDWSUSERS;unset GRPERRMSG");
}
private static String prepareUserList(List<Object> userNames) {
final String separator = " ";
StringBuilder buff = new StringBuilder();
for (Object name : userNames) {
String username = (String) name;
buff.append(username);
buff.append(separator);
}
return buff.toString();
}
protected static final String TMP_GROUPFILE_1 = "/tmp/wsgroup.$$";
protected static final String TMP_GROUPFILE_2 = "/tmp/wsgroup_work.$$";
protected static String getRemoveGroupTmpFiles(SolarisConnection conn) {
final String rmCmd = conn.buildCommand(false, "rm");
// @formatter:off
String removeGroupTmpFiles =
"if [ -f " + TMP_GROUPFILE_1 + " ]; then " +
rmCmd + " -f " + TMP_GROUPFILE_1 + "; " +
"fi; " +
"if [ -f " + TMP_GROUPFILE_2 + " ]; then " +
rmCmd + " -f " + TMP_GROUPFILE_2 + "; " +
"fi";
// @formatter:on
return removeGroupTmpFiles;
}
protected static String initGetOwner(final String file) {
// Add script to remove entry in passwd file
StringBuilder getOwner = new StringBuilder();
getOwner.append("OWNER=`ls -l ");
getOwner.append(file);
getOwner.append(" | awk '{ print $3 }'`; ");
getOwner.append("GOWNER=`ls -l ");
getOwner.append(file);
getOwner.append(" | awk '{ print $4 }'`");
return getOwner.toString();
}
/**
* Only {@link CreateNISGroup} and {@link UpdateNISGroup} use this
* functionality to process output of some specific commands.
*/
protected static void parseNisOutputForErrors(String out) {
if (StringUtil.isNotBlank(out)) {
if (out.contains(">")) {
out = out.substring(out.lastIndexOf(">") + 1);
out = out.trim();
}
if (StringUtil.isNotBlank(out)) {
throw new ConnectorException("ERROR: " + out);
}
}
}
}