summaryrefslogtreecommitdiffstats
path: root/base/server/cmscore/src/com/netscape/cmscore/dbs/LDAPDatabase.java
blob: 3e9f25e95ca1f41402bed2510b5139c452a57e5a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
package com.netscape.cmscore.dbs;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.Enumeration;
import java.util.Map;

import com.netscape.certsrv.apps.CMS;
import com.netscape.certsrv.base.EBaseException;
import com.netscape.certsrv.dbs.IDBAttrMapper;
import com.netscape.certsrv.dbs.IDBObj;
import com.netscape.certsrv.dbs.IDBRegistry;
import com.netscape.certsrv.dbs.IDBSSession;
import com.netscape.certsrv.dbs.IDBSearchResults;
import com.netscape.certsrv.dbs.IDBSubsystem;
import com.netscape.certsrv.dbs.Modification;
import com.netscape.certsrv.dbs.ModificationSet;
import com.netscape.cmsutil.ldap.LDAPUtil;

/**
 * This class implements LDAP database.
 *
 * @author Endi S. Dewata
 */
public abstract class LDAPDatabase<E extends IDBObj> extends Database<E> {

    public IDBSubsystem dbSubsystem;
    public String baseDN;
    public Class<E> recordType;

    public LDAPDatabase(String name, IDBSubsystem dbSubsystem, String baseDN, Class<E> recordType) throws EBaseException {
        super(name);

        this.dbSubsystem = dbSubsystem;
        this.baseDN = baseDN;
        this.recordType = recordType;

        register(recordType);
    }

    public IDBAttrMapper createMapper(Class<?> attrType, DBAttribute dbAttribute) throws EBaseException {
        String attrName = dbAttribute.value();

        if (attrType == String.class) {
            return new StringMapper(attrName);

        } else if (attrType == Date.class) {
            return new DateMapper(attrName);

        } else {
            // TODO: add other mappers
            throw new EBaseException("Unsupported attribute type: " + attrType);
        }
    }

    public void register(Class<E> recordType) throws EBaseException {

        CMS.debug("registering " + recordType.getName());

        IDBRegistry dbRegistry = dbSubsystem.getRegistry();

        // register object classes
        DBObjectClasses dbObjectClasses = recordType.getAnnotation(DBObjectClasses.class);
        if (dbObjectClasses == null) {
            throw new EBaseException("Missing object class mapping in " + recordType.getName());
        }
        dbRegistry.registerObjectClass(recordType.getName(), dbObjectClasses.value());

        // register attributes defined in setters/getters
        for (Method method : recordType.getMethods()) {
            DBAttribute dbAttribute = method.getAnnotation(DBAttribute.class);
            if (dbAttribute == null) continue;

            String name = method.getName();
            if (!name.matches("^set.+") && !name.matches("^get.+")) continue;

            // get attribute name from method name
            name = Character.toLowerCase(name.charAt(3)) + name.substring(4);

            Class<?> attrType = method.getReturnType();
            IDBAttrMapper mapper = createMapper(attrType, dbAttribute);

            dbRegistry.registerAttribute(name, mapper);
        }

        // register attributes defined in fields
        for (Field field : recordType.getFields()) {
            DBAttribute dbAttribute = field.getAnnotation(DBAttribute.class);
            if (dbAttribute == null) continue;

            String name = field.getName();
            Class<?> attrType = field.getType();
            IDBAttrMapper mapper = createMapper(attrType, dbAttribute);

            dbRegistry.registerAttribute(name, mapper);
        }
    }

    public abstract String createDN(String id);
    public abstract String createFilter(String keyword, Map<String, String> attributes);

    public void createFilter(StringBuilder sb, Map<String, String> attributes) {

        // if no attributes specified, don't change filter
        if (attributes == null || attributes.isEmpty()) return;

        // wrap current filter with attribute matching filter
        sb.insert(0, "(&");
        for (Map.Entry<String, String> entry : attributes.entrySet()) {
            sb.append("(" + entry.getKey() + "=" + LDAPUtil.escapeFilter(entry.getValue()) + ")");
        }
        sb.append(")");
    }

    @Override
    public Collection<E> findRecords(String keyword) throws Exception {
        return findRecords(keyword, null);
    }

    public Collection<E> findRecords(String keyword, Map<String, String> attributes) throws Exception {

        CMS.debug("LDAPDatabase: findRecords()");

        try (IDBSSession session = dbSubsystem.createSession()) {
            Collection<E> list = new ArrayList<E>();
            String ldapFilter = createFilter(keyword, attributes);
            CMS.debug("LDAPDatabase: searching " + baseDN + " with filter " + ldapFilter);
            IDBSearchResults results = session.search(baseDN, ldapFilter);

            while (results.hasMoreElements()) {
                @SuppressWarnings("unchecked")
                E result = (E)results.nextElement();
                list.add(result);
            }

            return list;
        }
    }

    @SuppressWarnings("unchecked")
    @Override
    public E getRecord(String id) throws Exception {
        CMS.debug("LDAPDatabase: getRecord(\"" + id + "\")");
        try (IDBSSession session = dbSubsystem.createSession()) {
            String dn = createDN(id);
            CMS.debug("LDAPDatabase: reading " + baseDN);
            return (E)session.read(dn);
        }
    }

    @Override
    public void addRecord(String id, E record) throws Exception {
        CMS.debug("LDAPDatabase: addRecord(\"" + id + "\")");
        try (IDBSSession session = dbSubsystem.createSession()) {
            String dn = createDN(id);

            CMS.debug("LDAPDatabase: adding " + dn);
            session.add(dn, record);
        }
    }

    @Override
    public void updateRecord(String id, E record) throws Exception {

        CMS.debug("LDAPDatabase: updateRecord(\"" + id + "\")");

        try (IDBSSession session = dbSubsystem.createSession()) {
            String dn = createDN(id);
            CMS.debug("LDAPDatabase: dn: " + dn);
            CMS.debug("LDAPDatabase: changetype: modify");

            ModificationSet mods = new ModificationSet();
            for (Enumeration<String> names = record.getSerializableAttrNames(); names.hasMoreElements(); ) {
                String name = names.nextElement();
                Object value = record.get(name);
                CMS.debug("LDAPDatabase: replace: " + name);
                CMS.debug("LDAPDatabase: " + name + ": " + value);
                CMS.debug("LDAPDatabase: -");
                mods.add(name, Modification.MOD_REPLACE, value);
            }

            session.modify(dn, mods);
            CMS.debug("LDAPDatabase: modification completed");

        } catch (Exception e) {
            CMS.debug("LDAPDatabase: modification failed");
            CMS.debug(e);
            throw e;
        }
    }

    @Override
    public void removeRecord(String id) throws Exception {
        CMS.debug("LDAPDatabase: removeRecord(\"" + id + "\")");
        try (IDBSSession session = dbSubsystem.createSession()) {
            String dn = createDN(id);

            CMS.debug("LDAPDatabase: removing " + dn);
            session.delete(dn);
        }
    }
}