BrightSide Workbench Full Report + Source Code
UserPushSubscription.java
Go to the documentation of this file.
1 /*
2  * TurrĂ³ i Cutiller Foundation. License notice.
3  * Copyright (C) 2019 Lluis TurrĂ³ Cutiller <http://www.turro.org/>
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU Affero General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU Affero General Public License for more details.
14  *
15  * You should have received a copy of the GNU Affero General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  */
18 package org.turro.push;
19 
20 import java.io.IOException;
21 import java.io.StringReader;
22 import java.security.GeneralSecurityException;
23 import java.security.NoSuchAlgorithmException;
24 import java.security.NoSuchProviderException;
25 import java.security.Security;
26 import java.security.spec.InvalidKeySpecException;
27 import java.util.List;
28 import java.util.Set;
29 import java.util.concurrent.ExecutionException;
30 import java.util.logging.Level;
31 import java.util.logging.Logger;
32 import javax.json.Json;
33 import javax.json.JsonObject;
34 import javax.json.JsonReader;
35 import org.turro.string.Strings;
36 import org.apache.hc.core5.http.HttpResponse;
37 import org.bouncycastle.jce.provider.BouncyCastleProvider;
38 import org.jose4j.lang.JoseException;
39 import org.turro.auth.Authentication;
40 import org.turro.collections.KeyValueMap;
41 import org.turro.elephant.context.ElephantContext;
42 import org.turro.elephant.context.IConstructor;
43 import org.turro.elephant.db.ElephantPU;
44 import org.turro.elephant.direct.AbstractDirect;
45 import org.turro.elephant.direct.DirectContent;
46 import org.turro.elephant.entities.db.PushSubscription;
47 import org.turro.lock.Initializer;
48 import org.turro.push.security.ServerKeys;
49 import org.turro.push.service.Notification;
50 import org.turro.push.service.PushService;
51 import org.turro.push.service.Subscription;
52 import org.turro.sql.SqlClause;
53 
58 @DirectContent(identifier="push-subscription")
59 public class UserPushSubscription extends AbstractDirect {
60 
61  // https://web-push-book.gauntface.com/
62 
63  public static void pushMessage(String idContact, String payload) {
64  subscriptions(idContact).forEach(pushSubscription -> {
65  pushMessage(pushSubscription, payload);
66  });
67  }
68 
69  public static void pushMessage(PushSubscription pushSubscription, String payload) {
70  try {
71  PushService pushService = instance();
73  pushSubscription.getPublicKey(), pushSubscription.getPrivateKey());
74  Subscription subscription = new Subscription(pushSubscription.getEndpoint(), keys);
75  Notification notification = new Notification(subscription, payload);
76  HttpResponse response = pushService.send(notification);
77  if(REMOVE_RESPONSES.contains(response.getCode())) {
78  removeSubscription(pushSubscription);
79  }
80  } catch (NoSuchAlgorithmException | NoSuchProviderException | InvalidKeySpecException ex) {
81  Logger.getLogger(UserPushSubscription.class.getName()).log(Level.SEVERE, null, ex);
82  } catch (GeneralSecurityException | IOException | JoseException | ExecutionException | InterruptedException ex) {
83  Logger.getLogger(UserPushSubscription.class.getName()).log(Level.SEVERE, null, ex);
84  }
85  }
86 
87  /* DirectContent */
88 
90  }
91 
92  @Override
93  protected String getIdentifier() {
94  return UserPushSubscription.class.getAnnotation(DirectContent.class).identifier();
95  }
96 
97  @Override
98  protected void doExecute(IConstructor constructor, KeyValueMap map) {
99  if(map.containsKey("json")) {
100  JsonReader reader = Json.createReader(new StringReader(map.get("json")));
101  JsonObject subs = reader.readObject();
102  JsonObject keys = subs.getJsonObject("keys");
103  PushSubscription subscription = new PushSubscription();
104  subscription.setContactId(Authentication.getIContact().getId());
105  subscription.setEndpoint(subs.getString("endpoint"));
106  subscription.setPrivateKey(keys.getString("auth"));
107  subscription.setPublicKey(keys.getString("p256dh"));
108  new ElephantPU().saveObject(subscription);
109  pushMessage(subscription, PushMessage.title("Welcome")
110  .message("Successfully subscribed on this device")
111  .click(ElephantContext.getServerUrl("http")).json());
112  }
113  }
114 
115  private static List<PushSubscription> subscriptions(String idContact) {
116  return SqlClause.select("ps").from("PushSubscription ps")
117  .startIf(!Strings.isBlank(idContact))
118  .where().equal("ps.contactId", idContact)
119  .endIf()
120  .dao(new ElephantPU())
121  .resultList(PushSubscription.class);
122  }
123 
124  private static void removeSubscription(PushSubscription subscription) {
125  new ElephantPU().deleteObject(subscription);
126  }
127 
128  /* Factory */
129 
130  private static final Initializer<PushService> PUSH_SERVICE = new Initializer<>();
131 
132  public static PushService instance() {
133  return PUSH_SERVICE.instance(() -> {
134  Security.addProvider(new BouncyCastleProvider());
135  try {
136  return new PushService(
139  "mailto:lluis@turro.org");
140  } catch (GeneralSecurityException ex) {
141  Logger.getLogger(UserPushSubscription.class.getName()).log(Level.SEVERE, null, ex);
142  return null;
143  }
144  });
145  }
146 
147  private static final Set<Integer> REMOVE_RESPONSES = Set.of(401, 404, 410);
148 
149 }
static String getServerUrl(String scheme)
static PushMessage title(String title)
PushMessage click(String target)
PushMessage message(String message)
void doExecute(IConstructor constructor, KeyValueMap map)
static void pushMessage(String idContact, String payload)
static void pushMessage(PushSubscription pushSubscription, String payload)
HttpResponse send(Notification notification)