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 @ 9516

History | View | Annotate | Download (11.1 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.SwingUtilities;
38
import org.apache.commons.codec.net.URLCodec;
39
import org.apache.commons.lang.StringUtils;
40
import org.apache.commons.lang3.mutable.MutableBoolean;
41
import org.gvsig.desktopopen.DesktopOpen;
42
import org.gvsig.json.Json;
43
import org.gvsig.online.lib.api.OnlineUserIdentificationRequester;
44
import org.gvsig.online.swing.scribejava.keycloak.callbacks.CallbackAuthorizationHandler;
45
import org.gvsig.online.swing.scribejava.keycloak.callbacks.CallbackLogoutHandler;
46
import org.gvsig.tools.library.impl.DefaultLibrariesInitializer;
47
import org.gvsig.tools.util.ToolsUtilLocator;
48
import org.slf4j.Logger;
49
import org.slf4j.LoggerFactory;
50

    
51
/**
52
 *
53
 * @author gvSIG Team
54
 */
55
@SuppressWarnings("UseSpecificCatch")
56
public class UserIdentificationKeycloak
57
        implements OnlineUserIdentificationRequester {
58

    
59
    private static final Logger LOGGER = LoggerFactory.getLogger(UserIdentificationKeycloak.class);
60

    
61
    private static final String GVSIGDESKTOP_APIKEY = "gvsigdesktop";
62

    
63
    private String userId;
64
    private String authorization;
65
    private final UserIdentificationKeycloakConfig config;
66
    private final Object monitor = new Object();
67

    
68
    public UserIdentificationKeycloak(UserIdentificationKeycloakConfig config) {
69
        this.config = config;
70
        this.userId = null;
71
        this.authorization = null;
72
    }
73

    
74
    public UserIdentificationKeycloakConfig getConfig() {
75
        return this.config;
76
    }
77

    
78
    @Override
79
    public boolean requestIdentification() {
80
        try {
81
            if (!SwingUtilities.isEventDispatchThread()) {
82
                try {
83
                    MutableBoolean r = new MutableBoolean();
84
                    SwingUtilities.invokeAndWait(() -> {
85
                        r.setValue(requestIdentification());
86
                    });
87
                    return r.booleanValue();
88
                } catch (Exception ex) {
89
                    return false;
90
                }
91
            }
92

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

    
96
            OAuth20Service service = createService(authCallback);
97
            
98
            logout(service, requestId);
99
            authorize(service, requestId);
100

    
101
            applicationToFront();
102
            return StringUtils.isNotBlank(this.authorization);
103
        } catch (Exception ex) {
104
            LOGGER.warn("Can't request authorization", ex);
105
            return false;
106
        }
107
    }
108

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

    
134
        final OAuth20Service service = new ServiceBuilder(apiKey)
135
                .apiSecretIsEmptyStringUnsafe()
136
                .defaultScope(scope)
137
                .callback(authCallback)
138
                .build(KeycloakApi.instance(baseUrl, realm));
139
        return service;
140
    }
141

    
142
    private void authorize(OAuth20Service service, String requestId) throws IOException, URISyntaxException {
143
        UserIdentificationKeycloakFactory factory = this.config.getFactory();
144
        String contextPath = "/auth_" + requestId;
145
        CallbackAuthorizationHandler callbackHandler = new CallbackAuthorizationHandler(this, service, contextPath);
146
//        LOGGER.info("authorize "+requestId);
147
        try {
148
            factory.addCallback(config, contextPath, callbackHandler);
149
            final String authorizationUrl = service.getAuthorizationUrl();
150
            browse(authorizationUrl);
151
            this.waitForResponse();
152
        } catch(Exception e) {
153
            LOGGER.warn("Can't authorize",e);
154
            
155
        } finally {
156
//            LOGGER.info("authorize finally2 "+requestId);
157
            callbackHandler.remove();
158
//            LOGGER.info("authorize finally2 "+requestId);
159
        }
160
    }
161

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

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

    
220
    public void waitForResponse() {
221
//        LOGGER.info("waitForTheResponse");
222
        synchronized (this.monitor) {
223
            try {
224
                this.monitor.wait(3 * 60 * 1000);
225
            } catch (InterruptedException ex) {
226

    
227
            }
228
        }
229
//        LOGGER.info("waitForTheResponse exit");
230
    }
231

    
232
    public void stopWaitingForResponse() {
233
//        LOGGER.info("stopWaitingForResponse");
234
        synchronized (this.monitor) {
235
            this.monitor.notifyAll();
236
        }
237
//        LOGGER.info("stopWaitingForResponse exit");
238
    }
239

    
240
    @Override
241
    public String getUserId() {
242
        return this.userId;
243
    }
244

    
245
    public void setUserId(String userId) {
246
        this.userId = userId;
247
    }
248

    
249
    @Override
250
    public String getAuthorization() {
251
        return this.authorization;
252
    }
253

    
254
    public void setAuthorization(String authorization) {
255
        this.authorization = authorization;
256
    }
257

    
258
    public static void main(String... args) throws Exception {
259
        new DefaultLibrariesInitializer().fullInitialize();
260

    
261
        UserIdentificationKeycloakFactory factory = new UserIdentificationKeycloakFactory();
262

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

    
265
        conf.setLocalPort(8889);
266
        conf.setBaseurl("https://keycloak.scolab.eu");
267
        conf.setRealm("joaquin");
268

    
269
        OnlineUserIdentificationRequester requester = conf.createUserIdentificationRequester();
270

    
271
        if (requester.requestIdentification()) {
272
            System.out.println("Identificacion concluida.");
273
            System.out.println("Usuario: " + requester.getUserId());
274
            System.out.println("Token  : " + requester.getAuthorization());
275
        } else {
276
            System.out.println("Identificacion fallida.");
277
        }
278

    
279
        Thread.sleep(4000);
280

    
281
        if (requester.requestIdentification()) {
282
            System.out.println("Identificacion concluida.");
283
            System.out.println("Usuario: " + requester.getUserId());
284
            System.out.println("Token  : " + requester.getAuthorization());
285
        } else {
286
            System.out.println("Identificacion fallida.");
287
        }
288

    
289
        factory.stopHttpServer(1);
290
    }
291
}