• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

square / keywhiz / 4793773343

pending completion
4793773343

Pull #1169

github

GitHub
Merge 1c53df345 into d865fa096
Pull Request #1169: Populating secret ownership via join rather than subquery

104 of 104 new or added lines in 3 files covered. (100.0%)

5406 of 7198 relevant lines covered (75.1%)

0.75 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.4
/server/src/main/java/keywhiz/service/daos/GroupDAO.java
1
/*
2
 * Copyright (C) 2015 Square, Inc.
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *      http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16

17
package keywhiz.service.daos;
18

19
import com.fasterxml.jackson.core.JsonProcessingException;
20
import com.fasterxml.jackson.databind.ObjectMapper;
21
import com.google.common.base.Throwables;
22
import com.google.common.collect.ImmutableMap;
23
import com.google.common.collect.ImmutableSet;
24
import java.time.OffsetDateTime;
25
import java.util.Optional;
26
import java.util.Set;
27
import java.util.stream.Collectors;
28
import javax.inject.Inject;
29
import keywhiz.api.model.Group;
30
import keywhiz.jooq.tables.Groups;
31
import keywhiz.jooq.tables.records.GroupsRecord;
32
import keywhiz.service.config.Readonly;
33
import org.jooq.Condition;
34
import org.jooq.Configuration;
35
import org.jooq.DSLContext;
36
import org.jooq.Record;
37
import org.jooq.Result;
38
import org.jooq.impl.DSL;
39

40
import static com.google.common.base.Preconditions.checkNotNull;
41
import static keywhiz.jooq.Tables.CLIENTS;
42
import static keywhiz.jooq.tables.Accessgrants.ACCESSGRANTS;
43
import static keywhiz.jooq.tables.DeletedSecrets.DELETED_SECRETS;
44
import static keywhiz.jooq.tables.Groups.GROUPS;
45
import static keywhiz.jooq.tables.Memberships.MEMBERSHIPS;
46
import static keywhiz.jooq.tables.Secrets.SECRETS;
47

48
public class GroupDAO {
49
  private static final Long NO_OWNER = null;
1✔
50

51
  private static final Groups GROUP_OWNERS = GROUPS.as("owners");
1✔
52

53
  private final DSLContext dslContext;
54
  private final GroupMapper groupMapper;
55
  private final ObjectMapper mapper;
56

57
  private GroupDAO(DSLContext dslContext, GroupMapper groupMapper, ObjectMapper mapper) {
1✔
58
    this.dslContext = dslContext;
1✔
59
    this.groupMapper = groupMapper;
1✔
60
    this.mapper = mapper;
1✔
61
  }
1✔
62

63
  public long createGroup(
64
      String name,
65
      String creator,
66
      String description,
67
      ImmutableMap<String, String> metadata) {
68
    return createGroup(
1✔
69
        name,
70
        creator,
71
        description,
72
        metadata,
73
        NO_OWNER);
74
  }
75

76
  public long createGroup(
77
      String name,
78
      String creator,
79
      String description,
80
      ImmutableMap<String, String> metadata,
81
      Long ownerId) {
82
    GroupsRecord r = dslContext.newRecord(GROUPS);
1✔
83

84
    String jsonMetadata;
85
    try {
86
      jsonMetadata = mapper.writeValueAsString(metadata);
1✔
87
    } catch (JsonProcessingException e) {
×
88
      // Serialization of a Map<String, String> can never fail.
89
      throw Throwables.propagate(e);
×
90
    }
1✔
91

92
    long now = OffsetDateTime.now().toEpochSecond();
1✔
93

94
    r.setName(name);
1✔
95
    r.setCreatedby(creator);
1✔
96
    r.setCreatedat(now);
1✔
97
    r.setUpdatedby(creator);
1✔
98
    r.setUpdatedat(now);
1✔
99
    r.setDescription(description);
1✔
100
    r.setMetadata(jsonMetadata);
1✔
101
    r.setOwner(ownerId);
1✔
102
    r.store();
1✔
103

104
    return r.getId();
1✔
105
  }
106

107
  public void deleteGroup(Group group) {
108
    dslContext.transaction(configuration -> {
1✔
109
      DSL.using(configuration)
1✔
110
              .delete(GROUPS)
1✔
111
              .where(GROUPS.ID.eq(group.getId()))
1✔
112
              .execute();
1✔
113
      DSL.using(configuration)
1✔
114
              .delete(MEMBERSHIPS)
1✔
115
              .where(MEMBERSHIPS.GROUPID.eq(group.getId()))
1✔
116
              .execute();
1✔
117
      DSL.using(configuration)
1✔
118
              .delete(ACCESSGRANTS)
1✔
119
              .where(ACCESSGRANTS.GROUPID.eq(group.getId()))
1✔
120
              .execute();
1✔
121
      DSL.using(configuration)
1✔
122
          .update(SECRETS)
1✔
123
          .set(SECRETS.OWNER, (Long) null)
1✔
124
          .where(SECRETS.OWNER.eq(group.getId()))
1✔
125
          .execute();
1✔
126
      DSL.using(configuration)
1✔
127
          .update(DELETED_SECRETS)
1✔
128
          .set(DELETED_SECRETS.OWNER, (Long) null)
1✔
129
          .where(DELETED_SECRETS.OWNER.eq(group.getId()))
1✔
130
          .execute();
1✔
131
      DSL.using(configuration)
1✔
132
          .update(GROUPS)
1✔
133
          .set(GROUPS.OWNER, (Long) null)
1✔
134
          .where(GROUPS.OWNER.eq(group.getId()))
1✔
135
          .execute();
1✔
136
      DSL.using(configuration)
1✔
137
          .update(CLIENTS)
1✔
138
          .set(CLIENTS.OWNER, (Long) null)
1✔
139
          .where(CLIENTS.OWNER.eq(group.getId()))
1✔
140
          .execute();
1✔
141
    });
1✔
142
  }
1✔
143

144
  public Optional<Group> getGroup(String name) {
145
    return getGroup(GROUPS.NAME.eq(name));
1✔
146
  }
147

148
  public Optional<Group> getGroupById(long id) {
149
    return getGroup(GROUPS.ID.eq(id));
1✔
150
  }
151

152
  private Optional<Group> getGroup(Condition condition) {
153
    Record record = dslContext
1✔
154
        .select(GROUPS.fields())
1✔
155
        .select(GROUP_OWNERS.ID, GROUP_OWNERS.NAME)
1✔
156
        .from(GROUPS)
1✔
157
        .leftJoin(GROUP_OWNERS)
1✔
158
        .on(GROUPS.OWNER.eq(GROUP_OWNERS.ID))
1✔
159
        .where(condition)
1✔
160
        .fetchOne();
1✔
161

162
    return Optional.ofNullable(recordToGroup(record));
1✔
163
  }
164

165
  private Group recordToGroup(Record record) {
166
    if (record == null) {
1✔
167
      return null;
1✔
168
    }
169

170
    GroupsRecord groupRecord = record.into(GROUPS);
1✔
171
    GroupsRecord ownerRecord = record.into(GROUP_OWNERS);
1✔
172

173
    boolean danglingOwner = groupRecord.getOwner() != null && ownerRecord.getId() == null;
1✔
174
    if (danglingOwner) {
1✔
175
      throw new IllegalStateException(
×
176
          String.format(
×
177
              "Owner %s for group %s is missing.",
178
              groupRecord.getOwner(),
×
179
              groupRecord.getName()));
×
180
    }
181

182
    Group group = groupMapper.map(groupRecord);
1✔
183
    if (ownerRecord != null) {
1✔
184
      group.setOwner(ownerRecord.getName());
1✔
185
    }
186

187
    return group;
1✔
188
  }
189

190
  public ImmutableSet<Group> getGroups() {
191
    Result<Record> results = dslContext
1✔
192
        .select(GROUPS.fields())
1✔
193
        .select(GROUP_OWNERS.NAME)
1✔
194
        .from(GROUPS)
1✔
195
        .leftJoin(GROUP_OWNERS)
1✔
196
        .on(GROUPS.OWNER.eq(GROUP_OWNERS.ID))
1✔
197
        .fetch();
1✔
198

199
    Set<Group> groups = results.stream()
1✔
200
        .map(this::recordToGroup)
1✔
201
        .collect(Collectors.toSet());
1✔
202

203
    return ImmutableSet.copyOf(groups);
1✔
204
  }
205

206
  public static class GroupDAOFactory implements DAOFactory<GroupDAO> {
207
    private final DSLContext jooq;
208
    private final DSLContext readonlyJooq;
209
    private final GroupMapper groupMapper;
210
    private final ObjectMapper mapper;
211

212
    @Inject public GroupDAOFactory(DSLContext jooq, @Readonly DSLContext readonlyJooq,
213
        GroupMapper groupMapper, ObjectMapper mapper) {
1✔
214
      this.jooq = jooq;
1✔
215
      this.readonlyJooq = readonlyJooq;
1✔
216
      this.groupMapper = groupMapper;
1✔
217
      this.mapper = mapper;
1✔
218
    }
1✔
219

220
    @Override public GroupDAO readwrite() {
221
      return new GroupDAO(jooq, groupMapper, mapper);
1✔
222
    }
223

224
    @Override public GroupDAO readonly() {
225
      return new GroupDAO(readonlyJooq, groupMapper, mapper);
1✔
226
    }
227

228
    @Override public GroupDAO using(Configuration configuration) {
229
      DSLContext dslContext = DSL.using(checkNotNull(configuration));
1✔
230
      return new GroupDAO(dslContext, groupMapper, mapper);
1✔
231
    }
232

233
    public GroupDAO using(DSLContext dslContext) {
234
      return new GroupDAO(dslContext, groupMapper, mapper);
×
235
    }
236
  }
237
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc