Browse code

FEATBL-880 Feat: Validate mailbox creation

SDS Proxy calls BM Core to validate a mailbox creation.
Idempotent as previously as BM Core validates everything.

Enguerran Colson authored on 04/10/2019 06:59:31
Showing 9 changed files
... ...
@@ -15,9 +15,15 @@ Require-Bundle: org.eclipse.core.runtime,
15 15
  com.google.guava,
16 16
  net.bluemind.eclipse.common,
17 17
  net.bluemind.metrics.registry,
18
- net.bluemind.vertx.common
18
+ net.bluemind.vertx.common,
19
+ net.bluemind.locator.client,
20
+ net.bluemind.core.rest,
21
+ net.bluemind.core.rest.http,
22
+ net.bluemind.network.topology,
23
+ net.bluemind.backend.mail.replica.api
19 24
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
20 25
 Automatic-Module-Name: net.bluemind.sds.proxy
21 26
 Bundle-ActivationPolicy: lazy
22 27
 Export-Package: net.bluemind.sds.proxy.dto,
23 28
  net.bluemind.sds.proxy.store
29
+Import-Package: net.bluemind.config
... ...
@@ -13,6 +13,9 @@
13 13
       <verticle
14 14
             impl="net.bluemind.sds.proxy.watchdog.WatchdogVerticle$Factory">
15 15
       </verticle>
16
+      <verticle
17
+            impl="net.bluemind.sds.proxy.events.SdsCyrusValidationHandlerVerticle$SdsCoreAPIFactory">
18
+      </verticle>
16 19
    </extension>
17 20
 
18 21
 </plugin>
... ...
@@ -33,6 +33,7 @@ import org.vertx.java.core.http.RouteMatcher;
33 33
 import org.vertx.java.core.json.JsonObject;
34 34
 import org.vertx.java.platform.Verticle;
35 35
 
36
+import com.google.common.base.Strings;
36 37
 import com.netflix.spectator.api.Id;
37 38
 import com.netflix.spectator.api.Registry;
38 39
 
... ...
@@ -81,6 +82,7 @@ public class SdsProxyHttpVerticle extends Verticle {
81 82
 		router.put("/sds", this::put);
82 83
 		router.get("/sds", this::get);
83 84
 		router.post("/configuration", this::configure);
85
+		router.head("/mailbox", this::validateMailbox);
84 86
 
85 87
 		srv.requestHandler(router).listen(8091, result -> {
86 88
 			if (result.succeeded()) {
... ...
@@ -112,6 +114,29 @@ public class SdsProxyHttpVerticle extends Verticle {
112 114
 		sendBody(req, SdsAddresses.GET, SdsResponse.class, (resp, http) -> http.setStatusCode(200).end());
113 115
 	}
114 116
 
117
+	private void validateMailbox(HttpServerRequest request) {
118
+		request.bodyHandler(buffer -> {
119
+			if (Strings.isNullOrEmpty(buffer.toString())) {
120
+				request.response().setStatusCode(403).end();
121
+			}
122
+
123
+			JsonObject json = new JsonObject(buffer.toString());
124
+
125
+			vertx.eventBus().sendWithTimeout(SdsAddresses.VALIDATION, json, 3000, result -> {
126
+				if (result.failed()) {
127
+					request.response().setStatusCode(200).end();
128
+				} else {
129
+					if ((boolean) result.result().body()) {
130
+						request.response().setStatusCode(200).end();
131
+					} else {
132
+						request.response().setStatusCode(403).end();
133
+					}
134
+				}
135
+
136
+			});
137
+		});
138
+	}
139
+
115 140
 	private <T extends SdsResponse> void sendBody(HttpServerRequest httpReq, String address, Class<T> respClass,
116 141
 			BiConsumer<T, HttpServerResponse> onSuccess) {
117 142
 		long start = registry.clock().monotonicTime();
... ...
@@ -32,6 +32,6 @@ public class SdsAddresses {
32 32
 
33 33
 	public static final String DELETE = "sds.delete";
34 34
 
35
-	public static final String VALIDATE = "sds.validate";
35
+	public static final String VALIDATION = "core.api.mailbox.validation";
36 36
 
37 37
 }
38 38
new file mode 100644
... ...
@@ -0,0 +1,76 @@
1
+package net.bluemind.sds.proxy.events;
2
+
3
+import java.util.Optional;
4
+
5
+import org.slf4j.Logger;
6
+import org.slf4j.LoggerFactory;
7
+import org.vertx.java.core.eventbus.Message;
8
+import org.vertx.java.core.json.JsonObject;
9
+import org.vertx.java.platform.Verticle;
10
+
11
+import net.bluemind.backend.mail.replica.api.ICyrusValidationPromise;
12
+import net.bluemind.config.Token;
13
+import net.bluemind.core.api.AsyncHandler;
14
+import net.bluemind.core.rest.http.HttpClientProvider;
15
+import net.bluemind.core.rest.http.ILocator;
16
+import net.bluemind.core.rest.http.VertxPromiseServiceProvider;
17
+import net.bluemind.lib.vertx.IVerticleFactory;
18
+import net.bluemind.network.topology.IServiceTopology;
19
+import net.bluemind.network.topology.Topology;
20
+import net.bluemind.network.topology.TopologyException;
21
+
22
+public class SdsCyrusValidationHandlerVerticle extends Verticle {
23
+	private static final Logger logger = LoggerFactory.getLogger(SdsCyrusValidationHandlerVerticle.class);
24
+	private static final int CORE_EXCEPTION = 0;
25
+	static ICyrusValidationPromise cli;
26
+
27
+	public static class SdsCoreAPIFactory implements IVerticleFactory {
28
+
29
+		@Override
30
+		public boolean isWorker() {
31
+			return false;
32
+		}
33
+
34
+		@Override
35
+		public Verticle newInstance() {
36
+			return new SdsCyrusValidationHandlerVerticle();
37
+		}
38
+	}
39
+
40
+	@Override
41
+	public void start() {
42
+		cli = getProvider();
43
+
44
+		vertx.eventBus().registerHandler(SdsAddresses.VALIDATION, (Message<JsonObject> message) -> {
45
+			JsonObject json = message.body();
46
+			String mailbox = json.getString("mailbox");
47
+			String partition = json.getString("partition");
48
+
49
+			cli.prevalidate(mailbox, partition).thenAccept((Boolean result) -> {
50
+				logger.info("BM Core {} creation of {}/{}", result ? "approves" : "rejects", partition, mailbox);
51
+				message.reply(result);
52
+			}).exceptionally(ex -> {
53
+				logger.error("Unable to get approval of {}/{}: {}", partition, mailbox, ex.getMessage());
54
+				message.fail(CORE_EXCEPTION, ex.getMessage());
55
+				return null;
56
+			});
57
+		});
58
+	}
59
+
60
+	private ICyrusValidationPromise getProvider() {
61
+		ILocator cachingLocator = (String service, AsyncHandler<String[]> asyncHandler) -> {
62
+			Optional<IServiceTopology> topology = Topology.getIfAvailable();
63
+			if (topology.isPresent()) {
64
+				String core = topology.get().core().value.address();
65
+				String[] resp = new String[] { core };
66
+				asyncHandler.success(resp);
67
+			} else {
68
+				asyncHandler.failure(new TopologyException("topology not available"));
69
+			}
70
+		};
71
+		HttpClientProvider clientProvider = new HttpClientProvider(vertx);
72
+		VertxPromiseServiceProvider provider = new VertxPromiseServiceProvider(clientProvider, cachingLocator,
73
+				Token.admin0());
74
+		return provider.instance("bm/core", ICyrusValidationPromise.class);
75
+	}
76
+}
0 77
new file mode 100644
... ...
@@ -0,0 +1,46 @@
1
+/* BEGIN LICENSE
2
+  * Copyright © Blue Mind SAS, 2012-2019
3
+  *
4
+  * This file is part of BlueMind. BlueMind is a messaging and collaborative
5
+  * solution.
6
+  *
7
+  * This program is free software; you can redistribute it and/or modify
8
+  * it under the terms of either the GNU Affero General Public License as
9
+  * published by the Free Software Foundation (version 3 of the License).
10
+  *
11
+  * This program is distributed in the hope that it will be useful,
12
+  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14
+  *
15
+  * See LICENSE.txt
16
+  * END LICENSE
17
+  */
18
+package net.bluemind.backend.mail.replica.api;
19
+
20
+import javax.ws.rs.GET;
21
+import javax.ws.rs.Path;
22
+import javax.ws.rs.QueryParam;
23
+
24
+import net.bluemind.core.api.BMApi;
25
+
26
+/**
27
+ * This internal API is called by sds-proxy.
28
+ *
29
+ */
30
+@BMApi(version = "3", internal = true)
31
+@Path("/cyrus_validation")
32
+public interface ICyrusValidation {
33
+
34
+	/**
35
+	 * Check if a new mailbox and its partition are valid as a mailbox for BlueMind.
36
+	 * 
37
+	 * @param mailbox   Cyrus internal name for a mailbox that must be approved by
38
+	 *                  the API (ex: global.virt!user.alice)
39
+	 * @param partition Cyrus internal name for a partition that must be approved by
40
+	 *                  the API (bm-master__global_virt)
41
+	 * @return true/false if the name of the mailbox is valid for a mailbox creation
42
+	 */
43
+	@GET
44
+	@Path("_prevalidate")
45
+	public boolean prevalidate(@QueryParam("mailbox") String mailbox, @QueryParam("partition") String partition);
46
+}
... ...
@@ -50,6 +50,9 @@
50 50
        <endpoint
51 51
              api="net.bluemind.backend.mail.api.IOutbox">
52 52
        </endpoint>
53
+       <endpoint
54
+             api="net.bluemind.backend.mail.replica.api.ICyrusValidation">
55
+       </endpoint>
53 56
    </extension>
54 57
    <extension
55 58
          point="net.bluemind.core.rest.serviceFactory">
... ...
@@ -95,6 +98,9 @@
95 98
       <serviceFactory
96 99
             class="net.bluemind.backend.mail.replica.service.OutboxServiceFactory">
97 100
       </serviceFactory>
101
+      <serviceFactory
102
+            class="net.bluemind.backend.mail.replica.service.CyrusValidationServiceFactory">
103
+      </serviceFactory>
98 104
    </extension>
99 105
    <extension
100 106
          point="net.bluemind.core.container.sharding.sharded">
101 107
new file mode 100644
... ...
@@ -0,0 +1,39 @@
1
+/* BEGIN LICENSE
2
+ * Copyright © Blue Mind SAS, 2012-2017
3
+ *
4
+ * This file is part of BlueMind. BlueMind is a messaging and collaborative
5
+ * solution.
6
+ *
7
+ * This program is free software; you can redistribute it and/or modify
8
+ * it under the terms of either the GNU Affero General Public License as
9
+ * published by the Free Software Foundation (version 3 of the License).
10
+ *
11
+ * This program is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14
+ *
15
+ * See LICENSE.txt
16
+ * END LICENSE
17
+ */
18
+package net.bluemind.backend.mail.replica.service;
19
+
20
+import net.bluemind.backend.mail.replica.api.ICyrusValidation;
21
+import net.bluemind.backend.mail.replica.service.internal.CyrusValidationService;
22
+import net.bluemind.core.api.fault.ServerFault;
23
+import net.bluemind.core.rest.BmContext;
24
+import net.bluemind.core.rest.ServerSideServiceProvider;
25
+
26
+public class CyrusValidationServiceFactory
27
+		implements ServerSideServiceProvider.IServerSideServiceFactory<ICyrusValidation> {
28
+
29
+	@Override
30
+	public Class<ICyrusValidation> factoryClass() {
31
+		return ICyrusValidation.class;
32
+	}
33
+
34
+	@Override
35
+	public ICyrusValidation instance(BmContext context, String... params) throws ServerFault {
36
+		return new CyrusValidationService();
37
+	}
38
+
39
+}
0 40
new file mode 100644
... ...
@@ -0,0 +1,26 @@
1
+package net.bluemind.backend.mail.replica.service.internal;
2
+
3
+import org.slf4j.Logger;
4
+import org.slf4j.LoggerFactory;
5
+
6
+import net.bluemind.backend.mail.replica.api.ICyrusValidation;
7
+
8
+public class CyrusValidationService implements ICyrusValidation {
9
+	private static final Logger logger = LoggerFactory.getLogger(CyrusValidationService.class);
10
+
11
+	@Override
12
+	public boolean prevalidate(String mailbox, String partition) {
13
+		logger.info("Cyrus Validation Service - prevalidate {}/{}", partition, mailbox);
14
+		boolean result = true;
15
+		switch (mailbox) {
16
+		case "default":
17
+			result = false;
18
+			break;
19
+		default:
20
+			result = true;
21
+			break;
22
+		}
23
+		return result;
24
+	}
25
+
26
+}