From 34442e410bd45ed531fe6bd397c3721ec67ddb7f Mon Sep 17 00:00:00 2001 From: Edgar Espina Date: Sun, 19 Feb 2017 22:15:08 -0300 Subject: [PATCH] OAuth error using MongoSessionStore fix #654 --- .../org/jooby/mongodb/MongoSessionStore.java | 75 +++++--- .../mongodb/MongodbSessionStoreTest.java | 168 ++++++++++++------ 2 files changed, 159 insertions(+), 84 deletions(-) diff --git a/jooby-mongodb/src/main/java/org/jooby/mongodb/MongoSessionStore.java b/jooby-mongodb/src/main/java/org/jooby/mongodb/MongoSessionStore.java index 8d8a91b748..3dbede9371 100644 --- a/jooby-mongodb/src/main/java/org/jooby/mongodb/MongoSessionStore.java +++ b/jooby-mongodb/src/main/java/org/jooby/mongodb/MongoSessionStore.java @@ -7,7 +7,7 @@ * "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 + * 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 @@ -18,25 +18,6 @@ */ package org.jooby.mongodb; -import static java.util.Objects.requireNonNull; - -import java.util.Date; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Optional; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; - -import javax.inject.Inject; -import javax.inject.Named; - -import org.bson.Document; -import org.bson.conversions.Bson; -import org.jooby.Session; -import org.jooby.Session.Builder; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import com.mongodb.client.MongoCollection; import com.mongodb.client.MongoCursor; import com.mongodb.client.MongoDatabase; @@ -46,6 +27,23 @@ import com.typesafe.config.Config; import com.typesafe.config.ConfigFactory; import com.typesafe.config.ConfigValueFactory; +import org.bson.Document; +import org.bson.conversions.Bson; +import org.jooby.Session; +import org.jooby.Session.Builder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.inject.Inject; +import javax.inject.Named; +import java.util.Date; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +import static java.util.Objects.requireNonNull; /** * A {@link Session.Store} powered by @@ -108,6 +106,14 @@ */ public class MongoSessionStore implements Session.Store { + private static final char DOT = '.'; + + private static final char UDOT = '\uFF0E'; + + private static final char DOLLAR = '$'; + + private static final char UDOLLAR = '\uFF04'; + private static final String SESSION_IDX = "_sessionIdx_"; /** The logging system. */ @@ -138,7 +144,7 @@ public MongoSessionStore(final MongoDatabase db, this(db, collection, seconds(timeout)); } - @SuppressWarnings({"unchecked", "rawtypes" }) + @SuppressWarnings({"unchecked", "rawtypes"}) @Override public Session get(final Builder builder) { return Optional.ofNullable(sessions.find(Filters.eq("_id", builder.sessionId())).first()) @@ -150,12 +156,12 @@ public Session get(final Builder builder) { Date savedAt = (Date) session.remove("_savedAt"); session.remove("_id"); - return builder + builder .accessedAt(accessedAt.getTime()) .createdAt(createdAt.getTime()) - .savedAt(savedAt.getTime()) - .set(session) - .build(); + .savedAt(savedAt.getTime()); + session.forEach((k, v) -> builder.set(decode(k.toString()), v.toString())); + return builder.build(); }).orElse(null); } @@ -172,7 +178,8 @@ public void save(final Session session) { .append("_createdAt", new Date(session.createdAt())) .append("_savedAt", new Date(session.savedAt())); // dump attributes - session.attributes().forEach((k, v) -> doc.append(k, v)); + Map attributes = session.attributes(); + attributes.forEach((k, v) -> doc.append(encode(k), v)); sessions.updateOne(filter, new Document("$set", doc), new UpdateOptions().upsert(true)); } @@ -235,4 +242,20 @@ private boolean existsIdx(final String name) { return false; } + private String encode(final String key) { + String value = key; + if (value.charAt(0) == DOLLAR) { + value = UDOLLAR + value.substring(1); + } + return value.replace(DOT, UDOT); + } + + private String decode(final String key) { + String value = key; + if (value.charAt(0) == UDOLLAR) { + value = DOLLAR + value.substring(1); + } + return value.replace(UDOT, DOT); + } + } diff --git a/jooby-mongodb/src/test/java/org/jooby/mongodb/MongodbSessionStoreTest.java b/jooby-mongodb/src/test/java/org/jooby/mongodb/MongodbSessionStoreTest.java index 5dd54b2055..d42b2d158c 100644 --- a/jooby-mongodb/src/test/java/org/jooby/mongodb/MongodbSessionStoreTest.java +++ b/jooby-mongodb/src/test/java/org/jooby/mongodb/MongodbSessionStoreTest.java @@ -1,23 +1,5 @@ package org.jooby.mongodb; -import static org.easymock.EasyMock.expect; -import static org.junit.Assert.assertEquals; - -import java.util.Date; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.concurrent.TimeUnit; - -import org.bson.Document; -import org.bson.conversions.Bson; -import org.jooby.Session; -import org.jooby.test.MockUnit; -import org.jooby.test.MockUnit.Block; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.powermock.core.classloader.annotations.PrepareForTest; -import org.powermock.modules.junit4.PowerMockRunner; - import com.google.common.collect.ImmutableMap; import com.mongodb.DBObject; import com.mongodb.client.FindIterable; @@ -29,13 +11,31 @@ import com.mongodb.client.model.IndexOptions; import com.mongodb.client.model.UpdateOptions; import com.mongodb.client.result.UpdateResult; +import org.bson.Document; +import org.bson.conversions.Bson; +import org.jooby.Session; +import org.jooby.test.MockUnit; +import org.jooby.test.MockUnit.Block; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +import java.util.Date; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.function.BiConsumer; + +import static org.easymock.EasyMock.expect; +import static org.junit.Assert.assertEquals; @RunWith(PowerMockRunner.class) @PrepareForTest({MongoSessionStore.class, IndexOptions.class, UpdateOptions.class, Filters.class, - LinkedHashMap.class }) + LinkedHashMap.class}) public class MongodbSessionStoreTest { - @SuppressWarnings({"unchecked", "rawtypes" }) + @SuppressWarnings({"unchecked", "rawtypes"}) MockUnit.Block boot = unit -> { MongoCollection collection = unit.get(MongoCollection.class); @@ -45,7 +45,7 @@ public class MongodbSessionStoreTest { long now = System.currentTimeMillis(); - Map attrs = ImmutableMap. of("k", "v"); + Map attrs = ImmutableMap.of("k.v", "v", "$d", "d"); @SuppressWarnings("rawtypes") MockUnit.Block saveSession = unit -> { @@ -65,7 +65,8 @@ public class MongodbSessionStoreTest { .append("_accessedAt", new Date(now)) .append("_createdAt", new Date(now)) .append("_savedAt", new Date(now)) - .append("k", "v"); + .append("k\uFF0Ev", "v") + .append("\uFF04d", "d"); UpdateOptions options = unit.constructor(UpdateOptions.class) .build(); @@ -225,47 +226,98 @@ public void saveSyncTtl() throws Exception { }); } - @SuppressWarnings({"unchecked", "rawtypes" }) + @SuppressWarnings({"unchecked", "rawtypes"}) @Test public void get() throws Exception { long now = System.currentTimeMillis(); new MockUnit(Session.class, Session.Builder.class, MongoDatabase.class, MongoCollection.class, DBObject.class) - .expect(boot) - .expect(unit -> { - Document doc = unit.mock(Document.class); - - Map sessionMap = unit.constructor(LinkedHashMap.class) - .args(Map.class) - .build(doc); - expect(sessionMap.remove("_accessedAt")).andReturn(new Date(now)); - expect(sessionMap.remove("_createdAt")).andReturn(new Date(now)); - expect(sessionMap.remove("_savedAt")).andReturn(new Date(now)); - expect(sessionMap.remove("_id")).andReturn("1234"); - - FindIterable result = unit.mock(FindIterable.class); - expect(result.first()).andReturn(doc); - - Session.Builder sb = unit.get(Session.Builder.class); - expect(sb.sessionId()).andReturn("1234"); - expect(sb.accessedAt(now)).andReturn(sb); - expect(sb.createdAt(now)).andReturn(sb); - expect(sb.savedAt(now)).andReturn(sb); - expect(sb.set(sessionMap)).andReturn(sb); - expect(sb.build()).andReturn(unit.get(Session.class)); - - Bson eq = unit.mock(Bson.class); - unit.mockStatic(Filters.class); - expect(Filters.eq("_id", "1234")).andReturn(eq); - - MongoCollection collection = unit.get(MongoCollection.class); - expect(collection.find(eq)).andReturn(result); - }) - .run(unit -> { - MongoSessionStore mss = new MongoSessionStore(unit.get(MongoDatabase.class), "sess", - "60"); - assertEquals(unit.get(Session.class), mss.get(unit.get(Session.Builder.class))); - }); + .expect(boot) + .expect(unit -> { + Document doc = unit.mock(Document.class); + + Map sessionMap = unit.constructor(LinkedHashMap.class) + .args(Map.class) + .build(doc); + expect(sessionMap.remove("_accessedAt")).andReturn(new Date(now)); + expect(sessionMap.remove("_createdAt")).andReturn(new Date(now)); + expect(sessionMap.remove("_savedAt")).andReturn(new Date(now)); + expect(sessionMap.remove("_id")).andReturn("1234"); + sessionMap.forEach(unit.capture(BiConsumer.class)); + + FindIterable result = unit.mock(FindIterable.class); + expect(result.first()).andReturn(doc); + + Session.Builder sb = unit.get(Session.Builder.class); + expect(sb.sessionId()).andReturn("1234"); + expect(sb.accessedAt(now)).andReturn(sb); + expect(sb.createdAt(now)).andReturn(sb); + expect(sb.savedAt(now)).andReturn(sb); + expect(sb.set("a.b", "c")).andReturn(sb); + expect(sb.build()).andReturn(unit.get(Session.class)); + + Bson eq = unit.mock(Bson.class); + unit.mockStatic(Filters.class); + expect(Filters.eq("_id", "1234")).andReturn(eq); + + MongoCollection collection = unit.get(MongoCollection.class); + expect(collection.find(eq)).andReturn(result); + }) + .run(unit -> { + MongoSessionStore mss = new MongoSessionStore(unit.get(MongoDatabase.class), "sess", + "60"); + assertEquals(unit.get(Session.class), mss.get(unit.get(Session.Builder.class))); + }, unit -> { + BiConsumer setter = unit.captured(BiConsumer.class).get(0); + setter.accept("a\uFF0Eb", "c"); + }); + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + @Test + public void getDollar() throws Exception { + long now = System.currentTimeMillis(); + new MockUnit(Session.class, Session.Builder.class, MongoDatabase.class, MongoCollection.class, + DBObject.class) + .expect(boot) + .expect(unit -> { + Document doc = unit.mock(Document.class); + + Map sessionMap = unit.constructor(LinkedHashMap.class) + .args(Map.class) + .build(doc); + expect(sessionMap.remove("_accessedAt")).andReturn(new Date(now)); + expect(sessionMap.remove("_createdAt")).andReturn(new Date(now)); + expect(sessionMap.remove("_savedAt")).andReturn(new Date(now)); + expect(sessionMap.remove("_id")).andReturn("1234"); + sessionMap.forEach(unit.capture(BiConsumer.class)); + + FindIterable result = unit.mock(FindIterable.class); + expect(result.first()).andReturn(doc); + + Session.Builder sb = unit.get(Session.Builder.class); + expect(sb.sessionId()).andReturn("1234"); + expect(sb.accessedAt(now)).andReturn(sb); + expect(sb.createdAt(now)).andReturn(sb); + expect(sb.savedAt(now)).andReturn(sb); + expect(sb.set("$ab", "c")).andReturn(sb); + expect(sb.build()).andReturn(unit.get(Session.class)); + + Bson eq = unit.mock(Bson.class); + unit.mockStatic(Filters.class); + expect(Filters.eq("_id", "1234")).andReturn(eq); + + MongoCollection collection = unit.get(MongoCollection.class); + expect(collection.find(eq)).andReturn(result); + }) + .run(unit -> { + MongoSessionStore mss = new MongoSessionStore(unit.get(MongoDatabase.class), "sess", + "60"); + assertEquals(unit.get(Session.class), mss.get(unit.get(Session.Builder.class))); + }, unit -> { + BiConsumer setter = unit.captured(BiConsumer.class).get(0); + setter.accept("\uFF04ab", "c"); + }); } @SuppressWarnings("rawtypes")