/*
* Copyright (c) 2008-2016 Computer Network Information Center (CNIC), Chinese Academy of Sciences.
*
* This file is part of Duckling project.
*
* Licensed 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 cn.vlabs.umt.services.user.dao.impl;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.naming.directory.BasicAttribute;
import javax.naming.directory.DirContext;
import javax.naming.directory.ModificationItem;
import javax.naming.directory.SearchControls;
import net.duckling.cloudy.common.CommonUtils;
import org.apache.log4j.Logger;
import org.springframework.ldap.InvalidAttributeValueException;
import org.springframework.ldap.core.ContextMapper;
import org.springframework.ldap.core.DirContextAdapter;
import org.springframework.ldap.core.DistinguishedName;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.ldap.filter.AndFilter;
import org.springframework.ldap.filter.EqualsFilter;
import cn.vlabs.umt.common.util.Config;
import cn.vlabs.umt.services.user.bean.AppSecret;
import cn.vlabs.umt.services.user.bean.LdapBean;
import cn.vlabs.umt.services.user.dao.ILdapAccessDAO;
import cn.vlabs.umt.services.user.service.ITransform;
public class LdapAccessDAOImpl implements ILdapAccessDAO {
private static final Logger LOG = Logger.getLogger(LdapAccessDAOImpl.class);
private LdapTemplate ldapTemplate;
private Config config;
private static final String aciAppAdminStr;
private static final String aciSuperAdminStr;
static {
StringBuffer sbAdmin = new StringBuffer();
sbAdmin.append("{");
sbAdmin.append(" identificationTag \"adminRole\",");
sbAdmin.append(" precedence 0,");
sbAdmin.append(" authenticationLevel none,");
sbAdmin.append(" itemOrUserFirst userFirst: ");
sbAdmin.append(" {");
sbAdmin.append(" userClasses ");
sbAdmin.append(" {");
sbAdmin.append(" userGroup { \"cn=Administrators,ou=groups,ou=system\" } ");
sbAdmin.append(" }");
sbAdmin.append(" ,");
sbAdmin.append(" userPermissions ");
sbAdmin.append(" {");
sbAdmin.append(" {");
sbAdmin.append(" protectedItems ");
sbAdmin.append(" {");
sbAdmin.append(" allUserAttributeTypesAndValues,");
sbAdmin.append(" allUserAttributeTypes,");
sbAdmin.append(" entry ");
sbAdmin.append(" }");
sbAdmin.append(" ,");
sbAdmin.append(" grantsAndDenials ");
sbAdmin.append(" {");
sbAdmin.append(" grantModify,");
sbAdmin.append(" grantRead,");
sbAdmin.append(" grantCompare,");
sbAdmin.append(" grantAdd,");
sbAdmin.append(" grantRemove,");
sbAdmin.append(" grantRename,");
sbAdmin.append(" grantBrowse,");
sbAdmin.append(" grantFilterMatch,");
sbAdmin.append(" grantDiscloseOnError,");
sbAdmin.append(" grantInvoke,");
sbAdmin.append(" grantReturnDN,");
sbAdmin.append(" grantExport,");
sbAdmin.append(" grantImport ");
sbAdmin.append(" }");
sbAdmin.append(" }");
sbAdmin.append(" }");
sbAdmin.append(" }");
sbAdmin.append("}");
aciSuperAdminStr = sbAdmin.toString();
StringBuffer sb = new StringBuffer();
sb.append("{");
sb.append(" identificationTag \"enableAllUsersRead\",");
sb.append(" precedence 0,");
sb.append(" authenticationLevel simple,");
sb.append(" itemOrUserFirst userFirst: ");
sb.append(" {");
sb.append(" userClasses { parentOfEntry },");
sb.append(" userPermissions ");
sb.append(" {");
sb.append(" {");
sb.append(" protectedItems { entry, allUserAttributeTypes },");
sb.append(" grantsAndDenials ");
sb.append(" {");
sb.append(" grantBrowse,");
sb.append(" grantFilterMatch,");
sb.append(" grantCompare,");
sb.append(" grantReturnDN,");
sb.append(" grantRead ");
sb.append(" }");
sb.append(" }");
sb.append(" }");
sb.append(" }");
sb.append("}");
aciAppAdminStr = sb.toString();
}
public void setLdapTemplate(LdapTemplate ldapTemplate) {
this.ldapTemplate = ldapTemplate;
}
public void setConfig(Config config) {
this.config = config;
}
@Override
public void addApp(LdapBean bean) {
String baseDn = config.getStringProp("ldap.base.dn", "");
if (CommonUtils.isNull(baseDn)) {
throw new RuntimeException("why i can't found the base dn for ldap");
}
// 创建app
DirContextAdapter context = new DirContextAdapter(baseDn);
DistinguishedName name = new DistinguishedName();
name.add("dc", bean.getRdn());
context.setDn(name);
context.setAttributeValues("objectClass", new String[] {
"organization", "dcObject", "top" });
context.setAttributeValue("dc", bean.getRdn());
context.setAttributeValue("o", bean.getRdn());
// TODO 修改这里,由内部来完成加密方式的选择
context.setAttributeValue("userPassword",
"{sha}" + bean.getLdapPassword());
context.setAttributeValue("administrativeRole",
"accessControlSpecificArea");
ldapTemplate.bind(context);
// 创建app的权限控制
DirContextAdapter contextPriv = new DirContextAdapter(baseDn);
name.add("cn", "readonly");
contextPriv.setDn(name);
contextPriv.setAttributeValues("objectClass", new String[] {
"accessControlSubentry", "subentry", "top" });
contextPriv.setAttributeValue("cn", "readonly");
contextPriv.setAttributeValue("prescriptiveACI",
aciAppAdminStr.toString());
contextPriv.setAttributeValue("subtreeSpecification", "{}");
ldapTemplate.bind(contextPriv);
DirContextAdapter contextSu = new DirContextAdapter(baseDn);
DistinguishedName suDN = new DistinguishedName();
suDN.add("dc", bean.getRdn());
suDN.add("cn", "su");
contextSu.setDn(suDN);
contextSu.setAttributeValues("objectClass", new String[] {
"accessControlSubentry", "subentry", "top" });
contextSu.setAttributeValue("cn", "su");
contextSu.setAttributeValue("prescriptiveACI",
aciSuperAdminStr.toString());
contextSu.setAttributeValue("subtreeSpecification", "{}");
ldapTemplate.bind(contextSu);
// bind group -- for linux
DirContextAdapter contextGroup = new DirContextAdapter(baseDn);
DistinguishedName groupDN = new DistinguishedName();
groupDN.add("dc", bean.getRdn());
groupDN.add("gidNumber", bean.getId() + "");
contextGroup.setDn(groupDN);
contextGroup.setAttributeValue("cn", bean.getRdn());
contextGroup.setAttributeValues("objectClass", new String[] {
"posixGroup", "top" });
ldapTemplate.bind(contextGroup);
}
@Override
public void addAccount(LdapBean bean, AppSecret as, String loginName) {
String baseDn = config.getStringProp("ldap.base.dn", "");
if (CommonUtils.isNull(baseDn)) {
throw new RuntimeException("why i can't found the base dn for ldap");
}
DirContextAdapter context = new DirContextAdapter(baseDn);
DistinguishedName name = new DistinguishedName();
name.add("dc", bean.getRdn());
name.add("uid", loginName);
context.setDn(name);
context.setAttributeValues("objectClass", new String[] {
"inetOrgPerson", "organizationalPerson", "posixAccount",
"person", "radiusAccount" });
context.setAttributeValue("cn", as.getUserCstnetId());
context.setAttributeValue("sn", as.getUserName());
context.setAttributeValue("homeDirectory",
"/home/" + loginName);
context.setAttributeValue("gidNumber", Integer.toString(bean.getId()));
context.setAttributeValue("uidNumber", Integer.toString(as.getUid()));
context.setAttributeValue("loginShell", "/bin/bash");
context.setAttributeValue("userPassword",
"{sha}" + as.getHashedSecret(ITransform.TYPE_SHA));
context.setAttributeValue("ntPassword",as.getHashedSecret(ITransform.TYPE_NT_HASH));
ldapTemplate.bind(context);
}
@Override
public void removeApp(String rdn) {
try {
DistinguishedName readonly = new DistinguishedName();
readonly.add("dc", rdn);
readonly.add("cn", "readonly");
ldapTemplate.unbind(readonly, true);
} catch (RuntimeException e) {
LOG.error("app readonly role[" + rdn + "] not found");
}
try {
DistinguishedName su = new DistinguishedName();
su.add("dc", rdn);
su.add("cn", "su");
ldapTemplate.unbind(su, true);
} catch (RuntimeException e) {
LOG.error("app su role[" + rdn + "] not found");
}
try {
DistinguishedName name = new DistinguishedName();
name.add("dc", rdn);
ldapTemplate.unbind(name, true);
} catch (RuntimeException e) {
LOG.error("app [" + rdn + "] not found");
}
}
@Override
public boolean updateSecret(String rdn, String orginLoginName, AppSecret secret) {
DistinguishedName dn = new DistinguishedName();
dn.add("dc", rdn);
dn.add("uid",secret.getUserLdapName());
try {
ModificationItem modifyUserPassword = new ModificationItem(
DirContext.REPLACE_ATTRIBUTE,
new BasicAttribute("userPassword", "{sha}"
+ secret.getHashedSecret(ITransform.TYPE_SHA)));
ModificationItem modifyNtPassword = new ModificationItem(
DirContext.REPLACE_ATTRIBUTE,
new BasicAttribute("ntPassword", secret.getHashedSecret(ITransform.TYPE_NT_HASH)));
ModificationItem modifyLdapName= new ModificationItem(
DirContext.REPLACE_ATTRIBUTE,
new BasicAttribute("uid", secret.getUserLdapName()));
Set<String> classSet = searchObjectClass(orginLoginName);
if (!classSet.contains("radiusAccount")){
ModificationItem addRadiusAccount = new ModificationItem(
DirContext.ADD_ATTRIBUTE,
new BasicAttribute("objectClass", "radiusAccount"));
ldapTemplate.modifyAttributes(dn,
new ModificationItem[] {addRadiusAccount,modifyLdapName, modifyUserPassword,modifyNtPassword});
}else{
ldapTemplate.modifyAttributes(dn,
new ModificationItem[] {modifyUserPassword,modifyLdapName, modifyNtPassword});
}
return true;
} catch (InvalidAttributeValueException e) {
return false;
}
}
@Override
public void removeSoAccount(String rdn, String loginName) {
DistinguishedName dn = new DistinguishedName();
dn.add("dc", rdn);
dn.add("uid", loginName);
try {
ldapTemplate.unbind(dn, true);
} catch (RuntimeException e) {
LOG.info("user [" + rdn + "] not found");
}
}
@SuppressWarnings("unchecked")
private Set<String> searchObjectClass(String ldapUid){
AndFilter af = new AndFilter();
af.and(new EqualsFilter("objectClass", "inetOrgPerson"));
af.and(new EqualsFilter("uid", ldapUid));
List<Object[]> list=ldapTemplate.search("", af.encode(),
SearchControls.SUBTREE_SCOPE, new ContextMapper() {
@Override
public Object mapFromContext(Object arg0) {
DirContextAdapter adap = (DirContextAdapter) arg0;
return adap.getObjectAttributes("objectClass");
}
});
HashSet<String> classSet = new HashSet<String>();
for(Object[] attr:list){
if (attr!=null){
for (Object objectClass:attr){
classSet.add((String)objectClass);
}
}
}
return classSet;
}
@SuppressWarnings("unchecked")
@Override
public List<String> searchDn(String ldapUid) {
AndFilter af = new AndFilter();
af.and(new EqualsFilter("objectClass", "inetOrgPerson"));
af.and(new EqualsFilter("uid", ldapUid));
return ldapTemplate.search("", af.encode(),
SearchControls.SUBTREE_SCOPE, new ContextMapper() {
@Override
public Object mapFromContext(Object arg0) {
DirContextAdapter adap = (DirContextAdapter) arg0;
return adap.getDn().toString();
}
});
}
@Override
public void removeByDn(String dn) {
ldapTemplate.unbind(dn);
}
}