Skip to content

Commit b541b27

Browse files
authored
Merge pull request #657 from jooby-project/654
OAuth error using MongoSessionStore fix #654
2 parents 9643dd5 + 34442e4 commit b541b27

2 files changed

Lines changed: 159 additions & 84 deletions

File tree

jooby-mongodb/src/main/java/org/jooby/mongodb/MongoSessionStore.java

Lines changed: 49 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* "License"); you may not use this file except in compliance
88
* with the License. You may obtain a copy of the License at
99
*
10-
* http://www.apache.org/licenses/LICENSE-2.0
10+
* http://www.apache.org/licenses/LICENSE-2.0
1111
*
1212
* Unless required by applicable law or agreed to in writing,
1313
* software distributed under the License is distributed on an
@@ -18,25 +18,6 @@
1818
*/
1919
package org.jooby.mongodb;
2020

21-
import static java.util.Objects.requireNonNull;
22-
23-
import java.util.Date;
24-
import java.util.LinkedHashMap;
25-
import java.util.Map;
26-
import java.util.Optional;
27-
import java.util.concurrent.TimeUnit;
28-
import java.util.concurrent.atomic.AtomicBoolean;
29-
30-
import javax.inject.Inject;
31-
import javax.inject.Named;
32-
33-
import org.bson.Document;
34-
import org.bson.conversions.Bson;
35-
import org.jooby.Session;
36-
import org.jooby.Session.Builder;
37-
import org.slf4j.Logger;
38-
import org.slf4j.LoggerFactory;
39-
4021
import com.mongodb.client.MongoCollection;
4122
import com.mongodb.client.MongoCursor;
4223
import com.mongodb.client.MongoDatabase;
@@ -46,6 +27,23 @@
4627
import com.typesafe.config.Config;
4728
import com.typesafe.config.ConfigFactory;
4829
import com.typesafe.config.ConfigValueFactory;
30+
import org.bson.Document;
31+
import org.bson.conversions.Bson;
32+
import org.jooby.Session;
33+
import org.jooby.Session.Builder;
34+
import org.slf4j.Logger;
35+
import org.slf4j.LoggerFactory;
36+
37+
import javax.inject.Inject;
38+
import javax.inject.Named;
39+
import java.util.Date;
40+
import java.util.LinkedHashMap;
41+
import java.util.Map;
42+
import java.util.Optional;
43+
import java.util.concurrent.TimeUnit;
44+
import java.util.concurrent.atomic.AtomicBoolean;
45+
46+
import static java.util.Objects.requireNonNull;
4947

5048
/**
5149
* A {@link Session.Store} powered by
@@ -108,6 +106,14 @@
108106
*/
109107
public class MongoSessionStore implements Session.Store {
110108

109+
private static final char DOT = '.';
110+
111+
private static final char UDOT = '\uFF0E';
112+
113+
private static final char DOLLAR = '$';
114+
115+
private static final char UDOLLAR = '\uFF04';
116+
111117
private static final String SESSION_IDX = "_sessionIdx_";
112118

113119
/** The logging system. */
@@ -138,7 +144,7 @@ public MongoSessionStore(final MongoDatabase db,
138144
this(db, collection, seconds(timeout));
139145
}
140146

141-
@SuppressWarnings({"unchecked", "rawtypes" })
147+
@SuppressWarnings({"unchecked", "rawtypes"})
142148
@Override
143149
public Session get(final Builder builder) {
144150
return Optional.ofNullable(sessions.find(Filters.eq("_id", builder.sessionId())).first())
@@ -150,12 +156,12 @@ public Session get(final Builder builder) {
150156
Date savedAt = (Date) session.remove("_savedAt");
151157
session.remove("_id");
152158

153-
return builder
159+
builder
154160
.accessedAt(accessedAt.getTime())
155161
.createdAt(createdAt.getTime())
156-
.savedAt(savedAt.getTime())
157-
.set(session)
158-
.build();
162+
.savedAt(savedAt.getTime());
163+
session.forEach((k, v) -> builder.set(decode(k.toString()), v.toString()));
164+
return builder.build();
159165
}).orElse(null);
160166
}
161167

@@ -172,7 +178,8 @@ public void save(final Session session) {
172178
.append("_createdAt", new Date(session.createdAt()))
173179
.append("_savedAt", new Date(session.savedAt()));
174180
// dump attributes
175-
session.attributes().forEach((k, v) -> doc.append(k, v));
181+
Map<String, String> attributes = session.attributes();
182+
attributes.forEach((k, v) -> doc.append(encode(k), v));
176183

177184
sessions.updateOne(filter, new Document("$set", doc), new UpdateOptions().upsert(true));
178185
}
@@ -235,4 +242,20 @@ private boolean existsIdx(final String name) {
235242
return false;
236243
}
237244

245+
private String encode(final String key) {
246+
String value = key;
247+
if (value.charAt(0) == DOLLAR) {
248+
value = UDOLLAR + value.substring(1);
249+
}
250+
return value.replace(DOT, UDOT);
251+
}
252+
253+
private String decode(final String key) {
254+
String value = key;
255+
if (value.charAt(0) == UDOLLAR) {
256+
value = DOLLAR + value.substring(1);
257+
}
258+
return value.replace(UDOT, DOT);
259+
}
260+
238261
}

jooby-mongodb/src/test/java/org/jooby/mongodb/MongodbSessionStoreTest.java

Lines changed: 110 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,5 @@
11
package org.jooby.mongodb;
22

3-
import static org.easymock.EasyMock.expect;
4-
import static org.junit.Assert.assertEquals;
5-
6-
import java.util.Date;
7-
import java.util.LinkedHashMap;
8-
import java.util.Map;
9-
import java.util.concurrent.TimeUnit;
10-
11-
import org.bson.Document;
12-
import org.bson.conversions.Bson;
13-
import org.jooby.Session;
14-
import org.jooby.test.MockUnit;
15-
import org.jooby.test.MockUnit.Block;
16-
import org.junit.Test;
17-
import org.junit.runner.RunWith;
18-
import org.powermock.core.classloader.annotations.PrepareForTest;
19-
import org.powermock.modules.junit4.PowerMockRunner;
20-
213
import com.google.common.collect.ImmutableMap;
224
import com.mongodb.DBObject;
235
import com.mongodb.client.FindIterable;
@@ -29,13 +11,31 @@
2911
import com.mongodb.client.model.IndexOptions;
3012
import com.mongodb.client.model.UpdateOptions;
3113
import com.mongodb.client.result.UpdateResult;
14+
import org.bson.Document;
15+
import org.bson.conversions.Bson;
16+
import org.jooby.Session;
17+
import org.jooby.test.MockUnit;
18+
import org.jooby.test.MockUnit.Block;
19+
import org.junit.Test;
20+
import org.junit.runner.RunWith;
21+
import org.powermock.core.classloader.annotations.PrepareForTest;
22+
import org.powermock.modules.junit4.PowerMockRunner;
23+
24+
import java.util.Date;
25+
import java.util.LinkedHashMap;
26+
import java.util.Map;
27+
import java.util.concurrent.TimeUnit;
28+
import java.util.function.BiConsumer;
29+
30+
import static org.easymock.EasyMock.expect;
31+
import static org.junit.Assert.assertEquals;
3232

3333
@RunWith(PowerMockRunner.class)
3434
@PrepareForTest({MongoSessionStore.class, IndexOptions.class, UpdateOptions.class, Filters.class,
35-
LinkedHashMap.class })
35+
LinkedHashMap.class})
3636
public class MongodbSessionStoreTest {
3737

38-
@SuppressWarnings({"unchecked", "rawtypes" })
38+
@SuppressWarnings({"unchecked", "rawtypes"})
3939
MockUnit.Block boot = unit -> {
4040
MongoCollection collection = unit.get(MongoCollection.class);
4141

@@ -45,7 +45,7 @@ public class MongodbSessionStoreTest {
4545

4646
long now = System.currentTimeMillis();
4747

48-
Map<String, String> attrs = ImmutableMap.<String, String> of("k", "v");
48+
Map<String, String> attrs = ImmutableMap.<String, String>of("k.v", "v", "$d", "d");
4949

5050
@SuppressWarnings("rawtypes")
5151
MockUnit.Block saveSession = unit -> {
@@ -65,7 +65,8 @@ public class MongodbSessionStoreTest {
6565
.append("_accessedAt", new Date(now))
6666
.append("_createdAt", new Date(now))
6767
.append("_savedAt", new Date(now))
68-
.append("k", "v");
68+
.append("k\uFF0Ev", "v")
69+
.append("\uFF04d", "d");
6970

7071
UpdateOptions options = unit.constructor(UpdateOptions.class)
7172
.build();
@@ -225,47 +226,98 @@ public void saveSyncTtl() throws Exception {
225226
});
226227
}
227228

228-
@SuppressWarnings({"unchecked", "rawtypes" })
229+
@SuppressWarnings({"unchecked", "rawtypes"})
229230
@Test
230231
public void get() throws Exception {
231232
long now = System.currentTimeMillis();
232233
new MockUnit(Session.class, Session.Builder.class, MongoDatabase.class, MongoCollection.class,
233234
DBObject.class)
234-
.expect(boot)
235-
.expect(unit -> {
236-
Document doc = unit.mock(Document.class);
237-
238-
Map sessionMap = unit.constructor(LinkedHashMap.class)
239-
.args(Map.class)
240-
.build(doc);
241-
expect(sessionMap.remove("_accessedAt")).andReturn(new Date(now));
242-
expect(sessionMap.remove("_createdAt")).andReturn(new Date(now));
243-
expect(sessionMap.remove("_savedAt")).andReturn(new Date(now));
244-
expect(sessionMap.remove("_id")).andReturn("1234");
245-
246-
FindIterable result = unit.mock(FindIterable.class);
247-
expect(result.first()).andReturn(doc);
248-
249-
Session.Builder sb = unit.get(Session.Builder.class);
250-
expect(sb.sessionId()).andReturn("1234");
251-
expect(sb.accessedAt(now)).andReturn(sb);
252-
expect(sb.createdAt(now)).andReturn(sb);
253-
expect(sb.savedAt(now)).andReturn(sb);
254-
expect(sb.set(sessionMap)).andReturn(sb);
255-
expect(sb.build()).andReturn(unit.get(Session.class));
256-
257-
Bson eq = unit.mock(Bson.class);
258-
unit.mockStatic(Filters.class);
259-
expect(Filters.eq("_id", "1234")).andReturn(eq);
260-
261-
MongoCollection collection = unit.get(MongoCollection.class);
262-
expect(collection.find(eq)).andReturn(result);
263-
})
264-
.run(unit -> {
265-
MongoSessionStore mss = new MongoSessionStore(unit.get(MongoDatabase.class), "sess",
266-
"60");
267-
assertEquals(unit.get(Session.class), mss.get(unit.get(Session.Builder.class)));
268-
});
235+
.expect(boot)
236+
.expect(unit -> {
237+
Document doc = unit.mock(Document.class);
238+
239+
Map sessionMap = unit.constructor(LinkedHashMap.class)
240+
.args(Map.class)
241+
.build(doc);
242+
expect(sessionMap.remove("_accessedAt")).andReturn(new Date(now));
243+
expect(sessionMap.remove("_createdAt")).andReturn(new Date(now));
244+
expect(sessionMap.remove("_savedAt")).andReturn(new Date(now));
245+
expect(sessionMap.remove("_id")).andReturn("1234");
246+
sessionMap.forEach(unit.capture(BiConsumer.class));
247+
248+
FindIterable result = unit.mock(FindIterable.class);
249+
expect(result.first()).andReturn(doc);
250+
251+
Session.Builder sb = unit.get(Session.Builder.class);
252+
expect(sb.sessionId()).andReturn("1234");
253+
expect(sb.accessedAt(now)).andReturn(sb);
254+
expect(sb.createdAt(now)).andReturn(sb);
255+
expect(sb.savedAt(now)).andReturn(sb);
256+
expect(sb.set("a.b", "c")).andReturn(sb);
257+
expect(sb.build()).andReturn(unit.get(Session.class));
258+
259+
Bson eq = unit.mock(Bson.class);
260+
unit.mockStatic(Filters.class);
261+
expect(Filters.eq("_id", "1234")).andReturn(eq);
262+
263+
MongoCollection collection = unit.get(MongoCollection.class);
264+
expect(collection.find(eq)).andReturn(result);
265+
})
266+
.run(unit -> {
267+
MongoSessionStore mss = new MongoSessionStore(unit.get(MongoDatabase.class), "sess",
268+
"60");
269+
assertEquals(unit.get(Session.class), mss.get(unit.get(Session.Builder.class)));
270+
}, unit -> {
271+
BiConsumer<String, String> setter = unit.captured(BiConsumer.class).get(0);
272+
setter.accept("a\uFF0Eb", "c");
273+
});
274+
}
275+
276+
@SuppressWarnings({"unchecked", "rawtypes"})
277+
@Test
278+
public void getDollar() throws Exception {
279+
long now = System.currentTimeMillis();
280+
new MockUnit(Session.class, Session.Builder.class, MongoDatabase.class, MongoCollection.class,
281+
DBObject.class)
282+
.expect(boot)
283+
.expect(unit -> {
284+
Document doc = unit.mock(Document.class);
285+
286+
Map sessionMap = unit.constructor(LinkedHashMap.class)
287+
.args(Map.class)
288+
.build(doc);
289+
expect(sessionMap.remove("_accessedAt")).andReturn(new Date(now));
290+
expect(sessionMap.remove("_createdAt")).andReturn(new Date(now));
291+
expect(sessionMap.remove("_savedAt")).andReturn(new Date(now));
292+
expect(sessionMap.remove("_id")).andReturn("1234");
293+
sessionMap.forEach(unit.capture(BiConsumer.class));
294+
295+
FindIterable result = unit.mock(FindIterable.class);
296+
expect(result.first()).andReturn(doc);
297+
298+
Session.Builder sb = unit.get(Session.Builder.class);
299+
expect(sb.sessionId()).andReturn("1234");
300+
expect(sb.accessedAt(now)).andReturn(sb);
301+
expect(sb.createdAt(now)).andReturn(sb);
302+
expect(sb.savedAt(now)).andReturn(sb);
303+
expect(sb.set("$ab", "c")).andReturn(sb);
304+
expect(sb.build()).andReturn(unit.get(Session.class));
305+
306+
Bson eq = unit.mock(Bson.class);
307+
unit.mockStatic(Filters.class);
308+
expect(Filters.eq("_id", "1234")).andReturn(eq);
309+
310+
MongoCollection collection = unit.get(MongoCollection.class);
311+
expect(collection.find(eq)).andReturn(result);
312+
})
313+
.run(unit -> {
314+
MongoSessionStore mss = new MongoSessionStore(unit.get(MongoDatabase.class), "sess",
315+
"60");
316+
assertEquals(unit.get(Session.class), mss.get(unit.get(Session.Builder.class)));
317+
}, unit -> {
318+
BiConsumer<String, String> setter = unit.captured(BiConsumer.class).get(0);
319+
setter.accept("\uFF04ab", "c");
320+
});
269321
}
270322

271323
@SuppressWarnings("rawtypes")

0 commit comments

Comments
 (0)