cbed2d477385fd570659728e36895eaa7c625af9
[aaa.git] / aaa-idm-store-h2 / src / main / java / org / opendaylight / aaa / datastore / h2 / DomainStore.java
1 /*
2  * Copyright (c) 2014, 2017 Hewlett-Packard Development Company, L.P. and others.  All rights reserved.
3  * Copyright (c) 2022 PANTHEON.tech, s.r.o.
4  *
5  * This program and the accompanying materials are made available under the
6  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
7  * and is available at http://www.eclipse.org/legal/epl-v10.html
8  */
9 package org.opendaylight.aaa.datastore.h2;
10
11 import static java.util.Objects.requireNonNull;
12
13 import com.google.common.annotations.VisibleForTesting;
14 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
15 import java.sql.ResultSet;
16 import java.sql.SQLException;
17 import java.sql.Statement;
18 import org.apache.commons.text.StringEscapeUtils;
19 import org.eclipse.jdt.annotation.NonNull;
20 import org.opendaylight.aaa.api.model.Domain;
21 import org.opendaylight.aaa.api.model.Domains;
22 import org.slf4j.Logger;
23 import org.slf4j.LoggerFactory;
24
25 /**
26  * Domain store.
27  */
28 final class DomainStore extends AbstractStore<Domain> {
29     private static final Logger LOG = LoggerFactory.getLogger(DomainStore.class);
30
31     /**
32      * Name of our SQL table. This constant lives here rather than in {@link SQLTable} for brevity.
33      */
34     static final @NonNull String TABLE = "AAA_DOMAINS";
35
36     static {
37         SQLTable.DOMAIN.verifyTable(TABLE);
38     }
39
40     /**
41      * Column storing {@link Domain#getDomainid()}, which is a flat namespace.
42      */
43     // FIXME: rename to "id"
44     @VisibleForTesting
45     static final String COL_ID = "domainid";
46     /**
47      * Column storing {@link Domain#getName()}, which is a short name.
48      */
49     @VisibleForTesting
50     static final String COL_NAME = "name";
51     /**
52      * Column storing {@link Domain#getDescription()}, which is a detailed description.
53      */
54     @VisibleForTesting
55     static final String COL_DESC = "description";
56     /**
57      * Column storing {@link Domain#isEnabled()}, which is ... not used anywhere.
58      */
59     // FIXME: remove or audit for potential callers of isEnabled()
60     @VisibleForTesting
61     static final String COL_ENABLED = "enabled";
62
63     DomainStore(final ConnectionProvider dbConnectionFactory) {
64         super(dbConnectionFactory, TABLE);
65     }
66
67     @Override
68     void createTable(final Statement stmt) throws SQLException {
69         stmt.executeUpdate("CREATE TABLE " + TABLE + " ("
70             // FIXME: on delete cascade? RoleStore.COL_DOMAIN_ID seems to reference this
71             + COL_ID      + " VARCHAR(128) PRIMARY KEY, "
72             + COL_NAME    + " VARCHAR(128) UNIQUE NOT NULL, "
73             + COL_DESC    + " VARCHAR(128), "
74             + COL_ENABLED + " BOOLEAN      NOT NULL)");
75     }
76
77     @Override
78     void cleanTable(final Statement stmt) throws SQLException {
79         stmt.execute("DELETE FROM " + TABLE);
80     }
81
82     @Override
83     protected Domain fromResultSet(final ResultSet rs) throws SQLException {
84         Domain domain = new Domain();
85         domain.setDomainid(rs.getString(COL_ID));
86         domain.setName(rs.getString(COL_NAME));
87         domain.setDescription(rs.getString(COL_DESC));
88         domain.setEnabled(rs.getBoolean(COL_ENABLED));
89         return domain;
90     }
91
92     Domains getDomains() throws StoreException {
93         Domains domains = new Domains();
94         domains.setDomains(listAll());
95         return domains;
96     }
97
98     // FIXME: seems to be unused
99     Domains getDomains(final String domainName) throws StoreException {
100         final Domains domains;
101         try (var conn = dbConnect();
102              var stmt = conn.prepareStatement("SELECT * FROM " + TABLE + " WHERE " + COL_NAME + " = ?")) {
103             stmt.setString(1, domainName);
104
105             domains = new Domains();
106             LOG.debug("getDomains() request: {}", stmt);
107             domains.setDomains(listFromStatement(stmt));
108         } catch (SQLException e) {
109             LOG.error("Error listing domains matching {}", domainName, e);
110             throw new StoreException("Error listing domains", e);
111         }
112         return domains;
113     }
114
115     Domain getDomain(final String id) throws StoreException {
116         try (var conn = dbConnect();
117              var stmt = conn.prepareStatement("SELECT * FROM " + TABLE + " WHERE " + COL_ID + " = ?")) {
118             stmt.setString(1, id);
119
120             LOG.debug("getDomain() request: {}", stmt);
121             return firstFromStatement(stmt);
122         } catch (SQLException e) {
123             LOG.error("Error retrieving domain {}", id, e);
124             throw new StoreException("Error loading domain", e);
125         }
126     }
127
128     Domain createDomain(final Domain domain) throws StoreException {
129         requireNonNull(domain);
130         requireNonNull(domain.getName());
131         requireNonNull(domain.isEnabled());
132         try (var conn = dbConnect();
133              var stmt = conn.prepareStatement("INSERT INTO " + TABLE + " ("
134                  + COL_ID + ", " + COL_NAME + ", " + COL_DESC + ", " + COL_ENABLED + ") VALUES (?, ?, ?, ?)")) {
135             stmt.setString(1, domain.getName());
136             stmt.setString(2, domain.getName());
137             stmt.setString(3, domain.getDescription());
138             stmt.setBoolean(4, domain.isEnabled());
139
140             LOG.debug("createDomain() request: {}", stmt);
141             if (stmt.executeUpdate() == 0) {
142                 throw new StoreException("Creating domain failed, no rows affected.");
143             }
144             domain.setDomainid(domain.getName());
145             return domain;
146         } catch (SQLException e) {
147             LOG.error("Error creating domain {}", domain.getName(), e);
148             throw new StoreException("Error creating domain", e);
149         }
150     }
151
152     Domain putDomain(final Domain domain) throws StoreException {
153         final var savedDomain = getDomain(domain.getDomainid());
154         if (savedDomain == null) {
155             return null;
156         }
157         if (domain.getDescription() != null) {
158             savedDomain.setDescription(domain.getDescription());
159         }
160         if (domain.getName() != null) {
161             savedDomain.setName(domain.getName());
162         }
163         if (domain.isEnabled() != null) {
164             savedDomain.setEnabled(domain.isEnabled());
165         }
166
167         try (var conn = dbConnect();
168              var stmt = conn.prepareStatement("UPDATE " + TABLE + " SET "
169                  + COL_NAME + " = ?, " + COL_DESC + " = ?, " + COL_ENABLED + " = ? WHERE " + COL_ID + " = ?")) {
170             stmt.setString(1, savedDomain.getName());
171             stmt.setString(2, savedDomain.getDescription());
172             stmt.setBoolean(3, savedDomain.isEnabled());
173             stmt.setString(4, savedDomain.getDomainid());
174
175             LOG.debug("putDomain() request: {}", stmt);
176             stmt.executeUpdate();
177         } catch (SQLException e) {
178             LOG.error("Error updating domain {}", domain.getDomainid(), e);
179             throw new StoreException("Error updating domain", e);
180         }
181
182         return savedDomain;
183     }
184
185     @SuppressFBWarnings(value = "SQL_NONCONSTANT_STRING_PASSED_TO_EXECUTE", justification = "Weird original code")
186     Domain deleteDomain(final String domainid) throws StoreException {
187         // FIXME: remove this once we have a more modern H2
188         final String escaped = StringEscapeUtils.escapeHtml4(domainid);
189         final var deletedDomain = getDomain(escaped);
190         if (deletedDomain == null) {
191             return null;
192         }
193
194         try (var conn = dbConnect();
195              var stmt = conn.createStatement()) {
196             // FIXME: prepare statement instead
197             final String query = String.format("DELETE FROM " + TABLE + " WHERE " + COL_ID + " = '%s'", escaped);
198             LOG.debug("deleteDomain() request: {}", query);
199
200             int deleteCount = stmt.executeUpdate(query);
201             LOG.debug("deleted {} records", deleteCount);
202             return deletedDomain;
203         } catch (SQLException e) {
204             LOG.error("Error deleting domain {}", domainid, e);
205             throw new StoreException("Error deleting domain", e);
206         }
207     }
208 }