Statistics
| Revision:

gvsig-projects-pool / org.gvsig.online / trunk / org.gvsig.online / org.gvsig.online.swing / org.gvsig.online.swing.scribejava / src / main / java / org / gvsig / online / swing / scribejava / keycloak / UserIdentificationKeycloak.java @ 9517

History | View | Annotate | Download (12.7 KB)

1
/*
2
 * gvSIG. Desktop Geographic Information System.
3
 * 
4
 * Copyright (C) 2007-2020 gvSIG Association.
5
 * 
6
 * This program is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU General Public License
8
 * as published by the Free Software Foundation; either version 3
9
 * of the License, or (at your option) any later version.
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.  See the
14
 * GNU General Public License for more details.
15
 * 
16
 * You should have received a copy of the GNU General Public License 
17
 * along with this program. If not, see <https://www.gnu.org/licenses/>. 
18
 * 
19
 * For any additional information, do not hesitate to contact us
20
 * at info AT gvsig.com, or visit our website www.gvsig.com.
21
 */
22
package org.gvsig.online.swing.scribejava.keycloak;
23

    
24
import com.github.scribejava.apis.KeycloakApi;
25
import com.github.scribejava.core.builder.ServiceBuilder;
26
import com.github.scribejava.core.model.OAuthRequest;
27
import com.github.scribejava.core.model.Response;
28
import com.github.scribejava.core.model.Verb;
29
import com.github.scribejava.core.oauth.OAuth20Service;
30
import java.awt.Frame;
31
import java.io.IOException;
32
import java.net.URI;
33
import java.net.URISyntaxException;
34
import java.util.UUID;
35
import java.util.concurrent.ExecutionException;
36
import javax.json.JsonObject;
37
import javax.swing.JOptionPane;
38
import javax.swing.SwingUtilities;
39
import org.apache.commons.codec.net.URLCodec;
40
import org.apache.commons.lang.StringUtils;
41
import org.apache.commons.lang3.mutable.MutableBoolean;
42
import org.gvsig.desktopopen.DesktopOpen;
43
import org.gvsig.json.Json;
44
import org.gvsig.online.lib.api.OnlineUserIdentificationRequester;
45
import org.gvsig.online.swing.scribejava.keycloak.callbacks.CallbackAuthorizationHandler;
46
import org.gvsig.online.swing.scribejava.keycloak.callbacks.CallbackLogoutHandler;
47
import org.gvsig.tools.ToolsLocator;
48
import org.gvsig.tools.i18n.I18nManager;
49
import org.gvsig.tools.library.impl.DefaultLibrariesInitializer;
50
import org.gvsig.tools.swing.api.ToolsSwingLocator;
51
import org.gvsig.tools.swing.api.threadsafedialogs.ThreadSafeDialogsManager;
52
import org.gvsig.tools.util.ToolsUtilLocator;
53
import org.slf4j.Logger;
54
import org.slf4j.LoggerFactory;
55

    
56
/**
57
 *
58
 * @author gvSIG Team
59
 */
60
@SuppressWarnings("UseSpecificCatch")
61
public class UserIdentificationKeycloak
62
        implements OnlineUserIdentificationRequester {
63

    
64
    private static final Logger LOGGER = LoggerFactory.getLogger(UserIdentificationKeycloak.class);
65

    
66
    private static final String GVSIGDESKTOP_APIKEY = "gvsigdesktop";
67

    
68
    private String userId;
69
    private String authorization;
70
    private final UserIdentificationKeycloakConfig config;
71
    private final Object monitor = new Object();
72
    private boolean stopedWaitingForResponse = false;
73
    private boolean userCancelledWaitingForResponse = false;
74

    
75
    public UserIdentificationKeycloak(UserIdentificationKeycloakConfig config) {
76
        this.config = config;
77
        this.userId = null;
78
        this.authorization = null;
79
    }
80

    
81
    public UserIdentificationKeycloakConfig getConfig() {
82
        return this.config;
83
    }
84

    
85
    @Override
86
    public boolean requestIdentification() {
87
        try {
88
            if (!SwingUtilities.isEventDispatchThread()) {
89
                try {
90
                    MutableBoolean r = new MutableBoolean();
91
                    SwingUtilities.invokeAndWait(() -> {
92
                        r.setValue(requestIdentification());
93
                    });
94
                    return r.booleanValue();
95
                } catch (Exception ex) {
96
                    return false;
97
                }
98
            }
99

    
100
            String requestId = StringUtils.remove(UUID.randomUUID().toString(), "-");
101
            String authCallback = "http://localhost:" + this.config.getLocalPort() + "/auth_" + requestId;
102

    
103
            OAuth20Service service = createService(authCallback);
104
            
105
            logout(service, requestId);
106
            if( !this.isUserCancelledWaitingForResponse() ) {
107
                authorize(service, requestId);
108
            }
109

    
110
            applicationToFront();
111
            return StringUtils.isNotBlank(this.authorization);
112
        } catch (Exception ex) {
113
            LOGGER.warn("Can't request authorization", ex);
114
            return false;
115
        }
116
    }
117

    
118
    private void applicationToFront() {
119
        try {
120
            java.awt.Window[] windows = Frame.getOwnerlessWindows();            
121
            if( windows != null ) {
122
                for (java.awt.Window window : windows) {
123
                    try {
124
                        window.toFront();
125
                    } catch(Exception ex) {
126
                        // Do nothing
127
                        LOGGER.debug("Can't bring to from "+window.toString());
128
                    }
129
                }
130
            }
131
        } catch(Exception e) {
132
            // Do nothing
133
            LOGGER.debug("Can't bring to from application");
134
        }
135
    }
136
    
137
    private OAuth20Service createService(String authCallback) {
138
        final String apiKey = GVSIGDESKTOP_APIKEY;
139
        final String baseUrl = this.config.getBaseurl();
140
        final String realm = this.config.getRealm();
141
        final String scope = "openid";
142

    
143
        final OAuth20Service service = new ServiceBuilder(apiKey)
144
                .apiSecretIsEmptyStringUnsafe()
145
                .defaultScope(scope)
146
                .callback(authCallback)
147
                .build(KeycloakApi.instance(baseUrl, realm));
148
        return service;
149
    }
150

    
151
    private void authorize(OAuth20Service service, String requestId) throws IOException, URISyntaxException {
152
        UserIdentificationKeycloakFactory factory = this.config.getFactory();
153
        String contextPath = "/auth_" + requestId;
154
        CallbackAuthorizationHandler callbackHandler = new CallbackAuthorizationHandler(this, service, contextPath);
155
//        LOGGER.info("authorize "+requestId);
156
        try {
157
            factory.addCallback(config, contextPath, callbackHandler);
158
            final String authorizationUrl = service.getAuthorizationUrl();
159
            browse(authorizationUrl);
160
            this.waitForResponse();
161
        } catch(Exception e) {
162
            LOGGER.warn("Can't authorize",e);
163
            
164
        } finally {
165
//            LOGGER.info("authorize finally2 "+requestId);
166
            callbackHandler.remove();
167
//            LOGGER.info("authorize finally2 "+requestId);
168
        }
169
    }
170

    
171
    private void logout(OAuth20Service service, String requestId) throws URISyntaxException, IOException {
172
        UserIdentificationKeycloakFactory factory = this.config.getFactory();
173
        String contextPath = "/logout_" + requestId;
174
        CallbackLogoutHandler callbackHandler = new CallbackLogoutHandler(this, service, contextPath);
175
//        LOGGER.info("logout "+requestId);
176
        try {
177
            URLCodec urlcodec = new URLCodec();
178
            String redirect_uri = urlcodec.encode("http://localhost:" + config.getLocalPort() + "/logout_"+requestId);
179
            String realm = urlcodec.encode(config.getRealm());
180
            String client_id = urlcodec.encode(GVSIGDESKTOP_APIKEY);
181
            
182
            factory.addCallback(config, contextPath, callbackHandler);
183
            String logoutUrl = String.format(
184
//                    "%s/auth/realms/%s/protocol/openid-connect/logout?post_logout_redirect_uri=%s&client_id=%s",
185
                    "%s/auth/realms/%s/protocol/openid-connect/logout?redirect_uri=%s&client_id=%s",
186
                    config.getBaseurl(),
187
                    realm,
188
                    redirect_uri,
189
                    client_id
190
            );
191
            
192
            browse(logoutUrl);
193
            this.waitForResponse();
194
        } catch(Exception e) {
195
            LOGGER.warn("Can't logout",e);
196
            
197
        } finally {
198
//            LOGGER.info("logout finally1 "+requestId);
199
            callbackHandler.remove();
200
//            LOGGER.info("logout finally2 "+requestId);
201
        }
202
    }
203
    
204
    private void browse(String url) throws URISyntaxException, IOException {
205
//        Desktop.getDesktop().browse(new URI(url));
206
        DesktopOpen desktop = ToolsUtilLocator.getToolsUtilManager().createDesktopOpen();
207
        desktop.browse(new URI(url));
208
    }
209

    
210
    public JsonObject  userInfo(OAuth20Service service) throws InterruptedException, IOException, ExecutionException {
211
//        LOGGER.info("userInfo");
212
        final String protectedResourceUrl = config.getBaseurl() + "/auth/realms/" + config.getRealm() + "/protocol/openid-connect/userinfo";
213
        final OAuthRequest request = new OAuthRequest(Verb.GET, protectedResourceUrl);
214
        service.signRequest(this.getAuthorization(), request);
215
        try (final Response response = service.execute(request)) {
216
            if (response.getCode() == 200) {
217
                JsonObject userinfo = Json.createObject(response.getBody());
218
//                    System.out.println("userid: " + userinfo.getString("preferred_username", null);
219
//                    System.out.println("name: " + userinfo.getString("name", null));
220
//                    System.out.println("email: " + userinfo.getString("email", null));
221
//                    System.out.println("roles: " + userinfo.get("gvsigol_roles").toString());
222
//                    System.out.println("grupos: " + userinfo.get("groups").toString());
223
                return userinfo;
224
            }
225
        }
226
        return null;
227
    }
228

    
229
    public void waitForResponse() {
230
//        LOGGER.info("waitForTheResponse");
231
        synchronized (this.monitor) {
232
            ThreadSafeDialogsManager dialogs = ToolsSwingLocator.getThreadSafeDialogsManager();
233
            I18nManager i18n = ToolsLocator.getI18nManager();
234
            try {
235
                this.stopedWaitingForResponse = false;
236
                this.userCancelledWaitingForResponse = false;
237
                while( !this.userCancelledWaitingForResponse && !this.stopedWaitingForResponse) {
238
                    this.monitor.wait(20 * 1000);
239
                    if( !this.stopedWaitingForResponse ) {
240
                        int n = dialogs.confirmDialog(
241
                                i18n.getTranslation("_Do_you_want_to_continue_waiting_for_the_authentication_to_complete"), 
242
                                i18n.getTranslation("_Authentication"), 
243
                                JOptionPane.YES_NO_OPTION, 
244
                                JOptionPane.QUESTION_MESSAGE
245
                        );
246
                        if( n == JOptionPane.NO_OPTION ) {
247
                            this.userCancelledWaitingForResponse = true;
248
                        }
249
                    }
250
                }
251
            } catch (InterruptedException ex) {
252

    
253
            }
254
        }
255
//        LOGGER.info("waitForTheResponse exit");
256
    }
257

    
258
    public void stopWaitingForResponse() {
259
//        LOGGER.info("stopWaitingForResponse");
260
        synchronized (this.monitor) {
261
            this.stopedWaitingForResponse = true;
262
            this.monitor.notifyAll();
263
        }
264
//        LOGGER.info("stopWaitingForResponse exit");
265
    }
266
    
267
    private boolean isUserCancelledWaitingForResponse() {
268
        return this.userCancelledWaitingForResponse;
269
    }
270

    
271
    @Override
272
    public String getUserId() {
273
        return this.userId;
274
    }
275

    
276
    public void setUserId(String userId) {
277
        this.userId = userId;
278
    }
279

    
280
    @Override
281
    public String getAuthorization() {
282
        return this.authorization;
283
    }
284

    
285
    public void setAuthorization(String authorization) {
286
        this.authorization = authorization;
287
    }
288

    
289
    public static void main(String... args) throws Exception {
290
        new DefaultLibrariesInitializer().fullInitialize();
291

    
292
        UserIdentificationKeycloakFactory factory = new UserIdentificationKeycloakFactory();
293

    
294
        UserIdentificationKeycloakConfig conf = factory.create("https://devel.gvsigonline.com/gvsigonline");
295

    
296
        conf.setLocalPort(8889);
297
        conf.setBaseurl("https://keycloak.scolab.eu");
298
        conf.setRealm("joaquin");
299

    
300
        OnlineUserIdentificationRequester requester = conf.createUserIdentificationRequester();
301

    
302
        if (requester.requestIdentification()) {
303
            System.out.println("Identificacion concluida.");
304
            System.out.println("Usuario: " + requester.getUserId());
305
            System.out.println("Token  : " + requester.getAuthorization());
306
        } else {
307
            System.out.println("Identificacion fallida.");
308
        }
309

    
310
        Thread.sleep(4000);
311

    
312
        if (requester.requestIdentification()) {
313
            System.out.println("Identificacion concluida.");
314
            System.out.println("Usuario: " + requester.getUserId());
315
            System.out.println("Token  : " + requester.getAuthorization());
316
        } else {
317
            System.out.println("Identificacion fallida.");
318
        }
319

    
320
        factory.stopHttpServer(1);
321
    }
322
}