Revision 9514

View differences:

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/callbacks/CallbackAuthorizationHandler.java
1
/*
2
 * To change this license header, choose License Headers in Project Properties.
3
 * To change this template file, choose Tools | Templates
4
 * and open the template in the editor.
5
 */
6
package org.gvsig.online.swing.scribejava.keycloak.callbacks;
7

  
8
import com.github.scribejava.core.model.OAuth2AccessToken;
9
import com.github.scribejava.core.model.OAuth2Authorization;
10
import com.github.scribejava.core.model.OAuthRequest;
11
import com.github.scribejava.core.model.Response;
12
import com.github.scribejava.core.model.Verb;
13
import com.github.scribejava.core.oauth.OAuth20Service;
14
import com.sun.net.httpserver.HttpExchange;
15
import com.sun.net.httpserver.HttpHandler;
16
import java.io.IOException;
17
import javax.json.JsonObject;
18
import org.gvsig.json.Json;
19
import org.gvsig.online.swing.scribejava.keycloak.UserIdentificationKeycloak;
20
import org.gvsig.online.swing.scribejava.keycloak.UserIdentificationKeycloakConfig;
21

  
22
/**
23
 *
24
 * @author jjdelcerro
25
 */
26
@SuppressWarnings("UseSpecificCatch")
27
public class CallbackAuthorizationHandler extends AbstractCallback implements HttpHandler {
28

  
29

  
30
    public CallbackAuthorizationHandler(UserIdentificationKeycloak identificationRequester, OAuth20Service service, String contextPath) {
31
        super(identificationRequester, service, contextPath);
32
    }
33

  
34
    @Override
35
    public void handle(HttpExchange t) throws IOException {
36
        String requestURL = "Unknown";
37
        try {
38
            requestURL = t.getRequestURI().toString();
39
            this.identificationRequester.setAuthorization(null);
40
            this.identificationRequester.setUserId(null);
41
            OAuth2Authorization auth = service.extractAuthorization(t.getRequestURI().toString());
42
            if (auth.getCode() == null) {
43
                response(t, 200, message("Can't autthenticate user\n"));
44
                return;
45
            }
46
            final OAuth2AccessToken accessToken = service.getAccessToken(auth.getCode());
47
            this.identificationRequester.setAuthorization(accessToken.getAccessToken());
48

  
49
            String userId = null;
50
            JsonObject userinfo = this.identificationRequester.userInfo(service);
51
            if (userinfo!=null) {
52
                userId = userinfo.getString("preferred_username", null);
53
//                    System.out.println("userid: " + userId);
54
//                    System.out.println("name: " + userinfo.getString("name", null));
55
//                    System.out.println("email: " + userinfo.getString("email", null));
56
//                    System.out.println("roles: " + userinfo.get("gvsigol_roles").toString());
57
//                    System.out.println("grupos: " + userinfo.get("groups").toString());
58
                this.identificationRequester.setUserId(userId);
59
            }
60
            response(t, 200, message_and_close((userId == null)?"User autthenticate\n":"User " + userId + " authenticated\n"));
61
        } catch (Exception ex) {
62
            LOGGER.warn("Can't process callback authorization (contextPath '" + contextPath + "', request URL '" + requestURL + "')", ex);
63
            responseQuietly(t, 500, message("Can't authenticate user\n" + ex.getMessage()));
64
        } finally {
65
            this.stopWaitingForResponse();
66
        }
67
    }
68
    
69
}
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/callbacks/AbstractCallback.java
1
/*
2
 * To change this license header, choose License Headers in Project Properties.
3
 * To change this template file, choose Tools | Templates
4
 * and open the template in the editor.
5
 */
6
package org.gvsig.online.swing.scribejava.keycloak.callbacks;
7

  
8
import com.github.scribejava.core.oauth.OAuth20Service;
9
import com.sun.net.httpserver.HttpExchange;
10
import java.io.IOException;
11
import java.io.OutputStream;
12
import org.apache.commons.lang3.StringUtils;
13
import org.gvsig.online.swing.scribejava.keycloak.UserIdentificationKeycloak;
14
import org.gvsig.online.swing.scribejava.keycloak.UserIdentificationKeycloakConfig;
15
import org.gvsig.online.swing.scribejava.keycloak.UserIdentificationKeycloakFactory;
16
import org.slf4j.Logger;
17
import org.slf4j.LoggerFactory;
18

  
19
/**
20
 *
21
 * @author jjdelcerro
22
 */
23
@SuppressWarnings("UseSpecificCatch")
24
public class AbstractCallback {
25

  
26
    protected static final Logger LOGGER = LoggerFactory.getLogger(CallbackAuthorizationHandler.class);
27

  
28
    protected final OAuth20Service service;
29
    protected final UserIdentificationKeycloak identificationRequester;
30
    protected final String contextPath;
31
    protected boolean aborted;
32

  
33
    public AbstractCallback(UserIdentificationKeycloak identificationRequester, OAuth20Service service, String contextPath) {
34
        this.contextPath = contextPath;
35
        this.service = service;
36
        this.identificationRequester = identificationRequester;
37
        this.aborted = false;
38
    }
39

  
40
    protected void response(HttpExchange t, int code, String body) throws IOException {
41
        t.sendResponseHeaders(code, body.length());
42
        try (final OutputStream os = t.getResponseBody()) {
43
            os.write(body.getBytes());
44
        }
45
    }
46

  
47
    protected void responseQuietly(HttpExchange t, int code, String body) {
48
        try {
49
            response(t, code, body);
50
        } catch (Exception e) {
51
            // Do nothing
52
            LOGGER.warn("Can't generate response.", e);
53
        }
54
    }
55

  
56
    public void remove() throws IOException {
57
        UserIdentificationKeycloakConfig config = identificationRequester.getConfig();
58
        UserIdentificationKeycloakFactory factory = config.getFactory();
59
        factory.removeCallback(config, contextPath);
60
        this.aborted = true;
61
    }
62

  
63
    public void stopWaitingForResponse() {
64
        if (aborted) {
65
            return;
66
        }
67
        this.identificationRequester.stopWaitingForResponse();
68
    }
69

  
70
    public String message_and_close(String s) {
71
        StringBuilder builder = new StringBuilder();
72
        builder.append("<html>\n");
73
        builder.append("<body >\n");
74
        builder.append(StringUtils.replace(s, "\n", "<br>\n"));
75
        builder.append("<script type=\"text/javascript\">");
76
        builder.append("window.close();");
77
        builder.append("</script>\n");
78
        builder.append("</body>\n");
79
        builder.append("</html>\n");
80
        return builder.toString();
81
    }
82

  
83
    public String message(String s) {
84
        StringBuilder builder = new StringBuilder();
85
        builder.append("<html>\n");
86
        builder.append("<body >\n");
87
        builder.append(StringUtils.replace(s, "\n", "<br>\n"));
88
        builder.append("</body>\n");
89
        builder.append("</html>\n");
90
        return builder.toString();
91
    }
92
}
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/callbacks/CallbackLogoutHandler.java
1
/*
2
 * To change this license header, choose License Headers in Project Properties.
3
 * To change this template file, choose Tools | Templates
4
 * and open the template in the editor.
5
 */
6
package org.gvsig.online.swing.scribejava.keycloak.callbacks;
7

  
8
import com.github.scribejava.core.oauth.OAuth20Service;
9
import com.sun.net.httpserver.HttpExchange;
10
import com.sun.net.httpserver.HttpHandler;
11
import java.io.IOException;
12
import java.io.OutputStream;
13
import org.gvsig.online.swing.scribejava.keycloak.UserIdentificationKeycloak;
14

  
15
/**
16
 *
17
 * @author jjdelcerro
18
 */
19
@SuppressWarnings("UseSpecificCatch")
20
public class CallbackLogoutHandler extends AbstractCallback implements HttpHandler {
21
    
22
    public CallbackLogoutHandler(UserIdentificationKeycloak identificationRequester, OAuth20Service service, String contextPath) {
23
        super(identificationRequester, service, contextPath);
24
    }
25

  
26
    @Override
27
    public void handle(HttpExchange t) throws IOException {
28
        String requestURL = "Unknown";
29
        try {
30
            requestURL = t.getRequestURI().toString();
31
//            LOGGER.info("Callback logout "+requestURL);
32
            response(t, 200, message_and_close("logout\n"+t.getRequestURI().toString()+"\n"));
33
        } catch (Exception ex) {
34
            LOGGER.warn("Can't process callback authorization (contextPath '" + contextPath + "', request URL '" + requestURL + "')", ex);
35
            responseQuietly(t, 500, message("Can't authenticate user<br>\n" + ex.getMessage()));
36
        } finally {
37
            this.stopWaitingForResponse();
38
        }
39
    }
40
    
41
    
42
}
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/UserIdentificationKeycloakConfig.java
1
/*
2
 * To change this license header, choose License Headers in Project Properties.
3
 * To change this template file, choose Tools | Templates
4
 * and open the template in the editor.
5
 */
6
package org.gvsig.online.swing.scribejava.keycloak;
7

  
8
import java.awt.event.ActionEvent;
9
import javax.json.JsonObject;
10
import org.apache.commons.lang3.StringUtils;
11
import org.gvsig.json.JsonObjectBuilder;
12
import org.gvsig.online.lib.api.OnlineLocator;
13
import org.gvsig.online.lib.api.OnlineUserIdentificationRequester;
14
import org.gvsig.online.lib.api.OnlineUserIdentificationRequester.OnlineUserIdentificationRequesterConfig;
15
import org.gvsig.online.lib.spi.AbstractOnlineUserIdentificationRequesterConfig;
16
import org.gvsig.tools.ToolsLocator;
17
import org.gvsig.tools.dynobject.DynStruct;
18
import org.gvsig.tools.persistence.PersistenceManager;
19
import org.gvsig.tools.persistence.Persistent;
20
import org.gvsig.tools.persistence.PersistentState;
21
import org.gvsig.tools.persistence.exception.PersistenceException;
22
import org.gvsig.tools.swing.api.ToolsSwingLocator;
23
import org.gvsig.tools.swing.api.windowmanager.Dialog;
24
import org.gvsig.tools.swing.api.windowmanager.WindowManager;
25
import org.gvsig.tools.swing.api.windowmanager.WindowManager_v2;
26
import org.gvsig.tools.util.LabeledValue;
27

  
28
/**
29
 *
30
 * @author jjdelcerro
31
 */
32
public class UserIdentificationKeycloakConfig 
33
        extends AbstractOnlineUserIdentificationRequesterConfig
34
        implements OnlineUserIdentificationRequesterConfig, Persistent, LabeledValue<OnlineUserIdentificationRequesterConfig>
35
    {
36
    
37
    private static final String PERSISTENT_DEFINITION_NAME = "UserIdentificationKeycloakConfig";
38
    
39
    private String realm;
40
    private String baseurl;
41
    private int localPort;
42
    
43
    public UserIdentificationKeycloakConfig(UserIdentificationKeycloakFactory factory, String urlOnline) {
44
        super(factory,urlOnline);
45
        this.localPort = 8888;
46
        this.realm = null;
47
        this.baseurl = null;
48
    }
49
    
50
    public UserIdentificationKeycloakFactory getFactory() {
51
        return (UserIdentificationKeycloakFactory) super.getFactory();
52
    }
53

  
54
    public int getLocalPort() {
55
        return localPort;
56
    }
57

  
58
    public String getBaseurl() {
59
        return baseurl;
60
    }
61

  
62
    public String getRealm() {
63
        return realm;
64
    }
65

  
66
    public void setBaseurl(String baseurl) {
67
        this.baseurl = baseurl;
68
    }
69

  
70
    public void setLocalPort(int localPort) {
71
        this.localPort = localPort;
72
    }
73

  
74
    public void setRealm(String realm) {
75
        this.realm = realm;
76
    }
77

  
78
    @Override
79
    public OnlineUserIdentificationRequester createUserIdentificationRequester() {
80
        if( StringUtils.isBlank(realm) ) {
81
            return null;
82
        }
83
        if( StringUtils.isBlank(baseurl) ) {
84
            return null;
85
        }
86
        if( localPort<1024  ) {
87
            return null;
88
        }
89
        return new UserIdentificationKeycloak(this);
90
    }
91

  
92
    @Override
93
    public boolean hasIdentificationConfig() {
94
        return true;
95
    }
96

  
97
    @Override
98
    public boolean requestIdentificationConfig() {
99
        WindowManager_v2 winmgr = (WindowManager_v2) ToolsSwingLocator.getWindowManager();
100
        UserIdentificationKeycloakConfigPanel panel = new UserIdentificationKeycloakConfigPanel();
101
        panel.put(this);
102
        Dialog dialog = winmgr.createDialog(
103
                panel,
104
                "Configuracion de Keycloak",
105
                null, 
106
                WindowManager_v2.BUTTONS_OK_CANCEL
107
        );
108
        dialog.addActionListener((ActionEvent e) -> {
109
            if( dialog.getAction()==WindowManager_v2.BUTTON_OK ) {
110
                panel.fetch(this);
111
            }
112
        });
113
        dialog.show(WindowManager.MODE.DIALOG);
114
        return dialog.getAction()==WindowManager_v2.BUTTON_OK;
115
    }
116

  
117
    @Override
118
    public void fromJson(JsonObject json) {
119
        super.fromJson(json);
120
        this.realm = json.getString("realm", null);
121
        this.baseurl = json.getString("baseurl", null);
122
        this.localPort = json.getInt("localPort", 0);                
123
    }
124

  
125
    @Override
126
    public JsonObjectBuilder toJsonBuilder() {
127
        JsonObjectBuilder builder = super.toJsonBuilder();
128
        builder.add("realm", this.realm);
129
        builder.add("baseurl", this.baseurl);
130
        builder.add("localPort", this.localPort);
131
        return null;
132
    }
133

  
134
    @Override
135
    public void saveToState(PersistentState state) throws PersistenceException {
136
        state.set("factory",this.factory.getName());
137
        state.set("urlOnline",this.urlOnline);
138
        state.set("baseurl",this.baseurl);
139
        state.set("realm",this.realm);
140
        state.set("localPort",this.localPort);
141
    }
142

  
143
    @Override
144
    public void loadFromState(PersistentState state) throws PersistenceException {
145
        String factoryName = state.getString("factory");
146
        this.factory = OnlineLocator.getOnlineManager().getUserIdentificationRequester(factoryName);
147
        this.urlOnline = state.getString("urlOnline");
148
        this.baseurl = state.getString("baseurl");
149
        this.realm = state.getString("realm");
150
        this.localPort = state.getInt("localPort");
151
    }
152
    
153
    public static void selfRegister() {
154
        PersistenceManager manager = ToolsLocator.getPersistenceManager();
155
        if (manager.getDefinition(PERSISTENT_DEFINITION_NAME) == null) {
156
            DynStruct definition = manager.addDefinition(
157
                    UserIdentificationKeycloakConfig.class,
158
                    PERSISTENT_DEFINITION_NAME, 
159
                    "UserIdentificationKeycloakConfig persistence definition", null, null
160
            );            
161
            definition.addDynFieldString("factory");
162
            definition.addDynFieldString("urlOnline");
163
            definition.addDynFieldString("baseurl");
164
            definition.addDynFieldString("realm");
165
            definition.addDynFieldInt("localPort");
166
        }
167
    }
168

  
169
    @Override
170
    public String getLabel() {
171
        return String.format("%s - %s", this.baseurl, this.realm);
172
    }
173

  
174
    @Override
175
    public OnlineUserIdentificationRequesterConfig getValue() {
176
        return this;
177
    }
178

  
179
    @Override
180
    public String toString() {
181
        return String.format(
182
           "Factory: %s\ngvSIG online URL: %s\nKeycloak URL:%s\nRealm: %s\nLocal port:%d", 
183
           this.getFactory().getName(),
184
           this.getUrlOnline(),
185
           this.getBaseurl(),
186
           this.getRealm(),
187
           this.getLocalPort()
188
        );
189
    }
190
        
191
    
192
}
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/UserIdentificationKeycloakConfigPanel.java
1
/*
2
 * To change this license header, choose License Headers in Project Properties.
3
 * To change this template file, choose Tools | Templates
4
 * and open the template in the editor.
5
 */
6
package org.gvsig.online.swing.scribejava.keycloak;
7

  
8
import java.awt.event.ActionEvent;
9
import java.awt.event.ActionListener;
10
import org.apache.commons.lang3.StringUtils;
11
import org.gvsig.tools.ToolsLocator;
12
import org.gvsig.tools.bookmarksandhistory.Bookmarks;
13
import org.gvsig.tools.bookmarksandhistory.BookmarksAndHistoryManager;
14
import org.gvsig.tools.bookmarksandhistory.History;
15
import org.gvsig.tools.swing.api.ToolsSwingLocator;
16
import org.gvsig.tools.swing.api.ToolsSwingManager;
17
import org.gvsig.tools.swing.api.bookmarkshistory.ActionEventWithCurrentValue;
18
import static org.gvsig.tools.swing.api.bookmarkshistory.ActionEventWithCurrentValue.ID_GETVALUE;
19
import static org.gvsig.tools.swing.api.bookmarkshistory.ActionEventWithCurrentValue.ID_SETVALUE;
20
import org.gvsig.tools.swing.api.bookmarkshistory.BookmarksController;
21
import org.gvsig.tools.swing.api.bookmarkshistory.HistoryController;
22
import org.gvsig.tools.swing.api.documentfilters.IntegerDocumentFilter;
23

  
24
/**
25
 *
26
 * @author jjdelcerro
27
 */
28
public class UserIdentificationKeycloakConfigPanel 
29
    extends UserIdentificationKeycloakConfigPanelView {
30
    
31
    private static final String BOOKMARKSANDHISTORY_NAME = "online_keykloak_config";
32
    
33
    private UserIdentificationKeycloakFactory factory;
34
//    private TextFieldWithHistoryAndBookmarkController urlKeycloakController;
35
    private final Bookmarks bookmarks;
36
    private final History history;
37
    private BookmarksController bookmarksController;
38
    private HistoryController historyController;
39
    
40
    
41
    
42
    public UserIdentificationKeycloakConfigPanel() {
43
        this.bookmarks = ToolsLocator.getBookmarksAndHistoryManager().getBookmarksGroup(BOOKMARKSANDHISTORY_NAME);
44
        this.history = ToolsLocator.getBookmarksAndHistoryManager().getHistoryGroup(BOOKMARKSANDHISTORY_NAME);
45
        initComponents();
46
    }
47
    
48
    public void put(UserIdentificationKeycloakConfig config) {
49
        this.factory = config.getFactory();
50
        this.txtUrlOnline.setText(config.getUrlOnline());
51
        this.txtUrlKeycloak.setText(config.getBaseurl());
52
        this.txtRealm.setText(config.getRealm());
53
        this.txtLocalPort.setText(String.valueOf(config.getLocalPort()));
54
    }
55
    
56
    public UserIdentificationKeycloakConfig fetch(UserIdentificationKeycloakConfig config) {
57
        if( config == null ) {
58
            config = this.factory.create(this.txtUrlOnline);
59
        }
60
        config.setBaseurl(StringUtils.defaultIfBlank(this.txtUrlKeycloak.getText(), null));
61
        config.setRealm(StringUtils.defaultIfBlank(this.txtRealm.getText(), null));
62
        config.setLocalPort(Integer.parseInt(this.txtLocalPort.getText()));
63
        this.history.add(config);
64
        return config;
65
    }
66
    
67
    public void initComponents() {
68
        ToolsSwingManager toolsSwingManager = ToolsSwingLocator.getToolsSwingManager();
69

  
70
        this.bookmarksController = ToolsSwingLocator.getToolsSwingManager().createBookmarksController(this.bookmarks, btnConfigKeycloakBookmarks);
71
        this.historyController = ToolsSwingLocator.getToolsSwingManager().createHistoryController(this.history, btnConfigKeycloakHistory);
72

  
73
        this.historyController.setFilter(null);
74

  
75
        ActionListener bookmarksAndHistoryListener = (ActionEvent e) -> {
76
                ActionEventWithCurrentValue<UserIdentificationKeycloakConfig> event = (ActionEventWithCurrentValue<UserIdentificationKeycloakConfig>) e;
77
                switch (event.getID()) {
78
                        case ID_GETVALUE:
79
                                event.setCurrentValue(fetch(null));
80
                                break;
81
                        case ID_SETVALUE:
82
                                put(event.getCurrentValue());
83
                                break;
84
                }
85
        };
86
        this.historyController.addActionListener(bookmarksAndHistoryListener);
87
        this.bookmarksController.addActionListener(bookmarksAndHistoryListener);
88
        
89
        
90
        toolsSwingManager.translate(lblLocalPort);
91
        toolsSwingManager.translate(lblRealm);
92
        toolsSwingManager.translate(lblUrlKeycloak);
93
        toolsSwingManager.translate(lblUrlOnline);
94
        
95
        this.txtUrlOnline.setEditable(false);
96
        
97
        toolsSwingManager.addClearButton(this.txtRealm);
98
        toolsSwingManager.addClearButton(this.txtLocalPort);
99
        toolsSwingManager.addClearButton(this.txtUrlKeycloak);
100
        
101
        toolsSwingManager.setDefaultPopupMenu(this.txtRealm);
102
        toolsSwingManager.setDefaultPopupMenu(this.txtLocalPort);
103
        toolsSwingManager.setDefaultPopupMenu(this.txtUrlKeycloak);
104
        
105
        IntegerDocumentFilter.install(txtLocalPort);
106
    }
107

  
108
}
109

  
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/UserIdentificationKeycloakFactory.java
1
package org.gvsig.online.swing.scribejava.keycloak;
2

  
3
import com.sun.net.httpserver.HttpExchange;
4
import com.sun.net.httpserver.HttpHandler;
5
import com.sun.net.httpserver.HttpServer;
6
import java.io.IOException;
7
import java.io.OutputStream;
8
import java.net.InetSocketAddress;
9
import org.gvsig.online.lib.api.OnlineLocator;
10
import org.gvsig.online.lib.api.OnlineManager;
11
import org.gvsig.online.lib.api.OnlineUserIdentificationRequester.OnlineUserIdentificationRequesterFactory;
12

  
13
/**
14
 *
15
 * @author jjdelcerro
16
 */
17
public class UserIdentificationKeycloakFactory implements OnlineUserIdentificationRequesterFactory {
18

  
19
    public static final String AUTH_MODE_KEYCLOAK = "Keycloak";
20
    
21
    private HttpServer httpserver;
22
    
23
    public UserIdentificationKeycloakFactory() {
24
        
25
    }
26
    
27
    @Override
28
    public String getName() {
29
        return AUTH_MODE_KEYCLOAK;
30
    }
31

  
32
    @Override
33
    public UserIdentificationKeycloakConfig create(Object... parameters) {
34
        return new UserIdentificationKeycloakConfig(this, (String) parameters[0]);
35
    }
36
    
37
    public HttpServer getHttpServer(UserIdentificationKeycloakConfig config) throws IOException {
38
        if( this.httpserver == null ) {
39
            this.httpserver = HttpServer.create(new InetSocketAddress(config.getLocalPort()), 0);
40
            this.httpserver.setExecutor(null); // creates a default executor
41
            this.httpserver.start();
42
        }
43
        return this.httpserver;
44
    }
45
    
46
    public void stopHttpServer(int delay) {
47
        if( this.httpserver!=null ) {
48
            this.httpserver.stop(0);
49
        }
50
    }
51

  
52
    public void addCallback(UserIdentificationKeycloakConfig config, String contextPath, HttpHandler handler ) throws IOException {
53
        this.getHttpServer(config).createContext(contextPath, handler);
54
    }
55

  
56
    public void removeCallback(UserIdentificationKeycloakConfig config, String contextPath) throws IOException {
57
        this.getHttpServer(config).removeContext(contextPath);
58
    }
59

  
60
    public static void selfRegister() {
61
        UserIdentificationKeycloakConfig.selfRegister();
62

  
63
        OnlineManager manager = OnlineLocator.getOnlineManager();
64
        manager.addUserIdentificationRequester(new UserIdentificationKeycloakFactory());
65
    }
66
}
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
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 org.gvsig.online.swing.scribejava.keycloak.callbacks.CallbackAuthorizationHandler;
25
import javax.swing.SwingUtilities;
26
import org.apache.commons.lang3.mutable.MutableBoolean;
27
import org.gvsig.online.lib.api.OnlineUserIdentificationRequester;
28

  
29
import com.github.scribejava.apis.KeycloakApi;
30
import com.github.scribejava.core.builder.ServiceBuilder;
31
import com.github.scribejava.core.model.OAuthRequest;
32
import com.github.scribejava.core.model.Response;
33
import com.github.scribejava.core.model.Verb;
34
import com.github.scribejava.core.oauth.OAuth20Service;
35
import java.awt.Desktop;
36
import java.awt.Frame;
37

  
38
import java.io.IOException;
39
import java.net.URI;
40
import java.net.URISyntaxException;
41
import java.util.UUID;
42
import java.util.concurrent.ExecutionException;
43
import javax.json.JsonObject;
44
import org.apache.commons.codec.net.URLCodec;
45
import org.apache.commons.lang.StringUtils;
46
import org.gvsig.json.Json;
47
import org.gvsig.online.swing.scribejava.keycloak.callbacks.CallbackLogoutHandler;
48
import org.gvsig.tools.library.impl.DefaultLibrariesInitializer;
49
import org.slf4j.Logger;
50
import org.slf4j.LoggerFactory;
51

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

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

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

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

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

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

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

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

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

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

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

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

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

  
163
    private void logout(OAuth20Service service, String requestId) throws URISyntaxException, IOException {
164
        UserIdentificationKeycloakFactory factory = this.config.getFactory();
165
        String contextPath = "/logout_" + requestId;
166
        CallbackLogoutHandler callbackHandler = new CallbackLogoutHandler(this, service, contextPath);
167
//        LOGGER.info("logout "+requestId);
168
        try {
169
            URLCodec urlcodec = new URLCodec();
170
            String redirect_uri = urlcodec.encode("http://localhost:" + config.getLocalPort() + "/logout_"+requestId);
171
            String realm = urlcodec.encode(config.getRealm());
172
            String client_id = urlcodec.encode(GVSIGDESKTOP_APIKEY);
173
            
174
            factory.addCallback(config, contextPath, callbackHandler);
175
            String logoutUrl = String.format(
176
                    "%s/auth/realms/%s/protocol/openid-connect/logout?post_logout_redirect_uri=%s&client_id=%s",
177
                    config.getBaseurl(),
178
                    realm,
179
                    redirect_uri,
180
                    client_id
181
            );
182
            
183
            Desktop.getDesktop().browse(new URI(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
    public JsonObject  userInfo(OAuth20Service service) throws InterruptedException, IOException, ExecutionException {
196
//        LOGGER.info("userInfo");
197
        final String protectedResourceUrl = config.getBaseurl() + "/auth/realms/" + config.getRealm() + "/protocol/openid-connect/userinfo";
198
        final OAuthRequest request = new OAuthRequest(Verb.GET, protectedResourceUrl);
199
        service.signRequest(this.getAuthorization(), request);
200
        try (final Response response = service.execute(request)) {
201
            if (response.getCode() == 200) {
202
                JsonObject userinfo = Json.createObject(response.getBody());
203
//                    System.out.println("userid: " + userinfo.getString("preferred_username", null);
204
//                    System.out.println("name: " + userinfo.getString("name", null));
205
//                    System.out.println("email: " + userinfo.getString("email", null));
206
//                    System.out.println("roles: " + userinfo.get("gvsigol_roles").toString());
207
//                    System.out.println("grupos: " + userinfo.get("groups").toString());
208
                return userinfo;
209
            }
210
        }
211
        return null;
212
    }
213

  
214
    public void waitForResponse() {
215
//        LOGGER.info("waitForTheResponse");
216
        synchronized (this.monitor) {
217
            try {
218
                this.monitor.wait(3 * 60 * 1000);
219
            } catch (InterruptedException ex) {
220

  
221
            }
222
        }
223
//        LOGGER.info("waitForTheResponse exit");
224
    }
225

  
226
    public void stopWaitingForResponse() {
227
//        LOGGER.info("stopWaitingForResponse");
228
        synchronized (this.monitor) {
229
            this.monitor.notifyAll();
230
        }
231
//        LOGGER.info("stopWaitingForResponse exit");
232
    }
233

  
234
    @Override
235
    public String getUserId() {
236
        return this.userId;
237
    }
238

  
239
    public void setUserId(String userId) {
240
        this.userId = userId;
241
    }
242

  
243
    @Override
244
    public String getAuthorization() {
245
        return this.authorization;
246
    }
247

  
248
    public void setAuthorization(String authorization) {
249
        this.authorization = authorization;
250
    }
251

  
252
    public static void main(String... args) throws Exception {
253
        new DefaultLibrariesInitializer().fullInitialize();
254

  
255
        UserIdentificationKeycloakFactory factory = new UserIdentificationKeycloakFactory();
256

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

  
259
        conf.setLocalPort(8888);
260
        conf.setBaseurl("https://keycloak.scolab.eu");
261
        conf.setRealm("joaquin");
262

  
263
        OnlineUserIdentificationRequester requester = conf.createUserIdentificationRequester();
264

  
265
        if (requester.requestIdentification()) {
266
            System.out.println("Identificacion concluida.");
267
            System.out.println("Usuario: " + requester.getUserId());
268
            System.out.println("Token  : " + requester.getAuthorization());
269
        } else {
270
            System.out.println("Identificacion fallida.");
271
        }
272

  
273
        Thread.sleep(4000);
274

  
275
        if (requester.requestIdentification()) {
276
            System.out.println("Identificacion concluida.");
277
            System.out.println("Usuario: " + requester.getUserId());
278
            System.out.println("Token  : " + requester.getAuthorization());
279
        } else {
280
            System.out.println("Identificacion fallida.");
281
        }
282

  
283
        factory.stopHttpServer(1);
284
    }
285
}
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/UserIdentificationKeycloakConfigPanelView.xml
1
<?xml version="1.0" encoding="UTF-8"?>
2

  
3
<object classname="com.jeta.forms.store.memento.FormPackage">
4
 <at name="fileversion">
5
  <object classname="com.jeta.forms.store.memento.FormsVersion2">
6
   <at name="major">2</at>
7
   <at name="minor">0</at>
8
   <at name="sub">0</at>
9
  </object>
10
 </at>
11
 <at name="form">
12
  <object classname="com.jeta.forms.store.memento.FormMemento">
13
   <super classname="com.jeta.forms.store.memento.ComponentMemento">
14
    <at name="cellconstraints">
15
     <object classname="com.jeta.forms.store.memento.CellConstraintsMemento">
16
      <at name="column">1</at>
17
      <at name="row">1</at>
18
      <at name="colspan">1</at>
19
      <at name="rowspan">1</at>
20
      <at name="halign">default</at>
21
      <at name="valign">default</at>
22
      <at name="insets" object="insets">0,0,0,0</at>
23
     </object>
24
    </at>
25
    <at name="componentclass">com.jeta.forms.gui.form.FormComponent</at>
26
   </super>
27
   <at name="id">/home/jjdelcerro/datos/devel/org.gvsig.online/org.gvsig.online.swing/org.gvsig.online.swing.scribejava/src/main/java/org/gvsig/online/swing/scribejava/login/UserIdentificationKeycloakConfigPanelView.xml</at>
28
   <at name="path">datos/devel/org.gvsig.online/org.gvsig.online.swing/org.gvsig.online.swing.scribejava/src/main/java/org/gvsig/online/swing/scribejava/login/UserIdentificationKeycloakConfigPanelView.xml</at>
29
   <at name="rowspecs">CENTER:2DLU:NONE,CENTER:DEFAULT:NONE,CENTER:2DLU:NONE,CENTER:DEFAULT:NONE,CENTER:2DLU:NONE,CENTER:DEFAULT:NONE,CENTER:2DLU:NONE,CENTER:DEFAULT:NONE,CENTER:2DLU:NONE,CENTER:DEFAULT:NONE,CENTER:2DLU:NONE</at>
30
   <at name="colspecs">FILL:4DLU:NONE,FILL:DEFAULT:NONE,FILL:4DLU:NONE,FILL:DEFAULT:GROW(1.0),FILL:4DLU:NONE</at>
31
   <at name="components">
32
    <object classname="java.util.LinkedList">
33
     <item >
34
      <at name="value">
35
       <object classname="com.jeta.forms.store.memento.BeanMemento">
36
        <super classname="com.jeta.forms.store.memento.ComponentMemento">
37
         <at name="cellconstraints">
38
          <object classname="com.jeta.forms.store.memento.CellConstraintsMemento">
39
           <at name="column">2</at>
40
           <at name="row">6</at>
41
           <at name="colspan">1</at>
42
           <at name="rowspan">1</at>
43
           <at name="halign">default</at>
44
           <at name="valign">default</at>
45
           <at name="insets" object="insets">0,0,0,0</at>
46
          </object>
47
         </at>
48
         <at name="componentclass">com.jeta.forms.gui.form.StandardComponent</at>
49
        </super>
50
        <at name="jetabeanclass">com.jeta.forms.gui.beans.JETABean</at>
51
        <at name="beanclass">com.jeta.forms.components.label.JETALabel</at>
52
        <at name="beanproperties">
53
         <object classname="com.jeta.forms.store.memento.PropertiesMemento">
54
          <at name="classname">com.jeta.forms.components.label.JETALabel</at>
55
          <at name="properties">
56
           <object classname="com.jeta.forms.store.support.PropertyMap">
57
            <at name="border">
58
             <object classname="com.jeta.forms.store.properties.CompoundBorderProperty">
59
              <super classname="com.jeta.forms.store.properties.BorderProperty">
60
               <at name="name">border</at>
61
              </super>
62
              <at name="borders">
63
               <object classname="java.util.LinkedList">
64
                <item >
65
                 <at name="value">
66
                  <object classname="com.jeta.forms.store.properties.DefaultBorderProperty">
67
                   <super classname="com.jeta.forms.store.properties.BorderProperty">
68
                    <at name="name">border</at>
69
                   </super>
70
                  </object>
71
                 </at>
72
                </item>
73
               </object>
74
              </at>
75
             </object>
76
            </at>
77
            <at name="name">lblUrlKeycloak</at>
78
            <at name="width">102</at>
79
            <at name="text">_URL_Keycloak</at>
80
            <at name="fill">
81
             <object classname="com.jeta.forms.store.properties.effects.PaintProperty">
82
              <at name="name">fill</at>
83
             </object>
84
            </at>
85
            <at name="height">16</at>
86
           </object>
87
          </at>
88
         </object>
89
        </at>
90
       </object>
91
      </at>
92
     </item>
93
     <item >
94
      <at name="value">
95
       <object classname="com.jeta.forms.store.memento.BeanMemento">
96
        <super classname="com.jeta.forms.store.memento.ComponentMemento">
97
         <at name="cellconstraints">
98
          <object classname="com.jeta.forms.store.memento.CellConstraintsMemento">
99
           <at name="column">2</at>
100
           <at name="row">8</at>
101
           <at name="colspan">1</at>
102
           <at name="rowspan">1</at>
103
           <at name="halign">default</at>
104
           <at name="valign">default</at>
105
           <at name="insets" object="insets">0,0,0,0</at>
106
          </object>
107
         </at>
108
         <at name="componentclass">com.jeta.forms.gui.form.StandardComponent</at>
109
        </super>
110
        <at name="jetabeanclass">com.jeta.forms.gui.beans.JETABean</at>
111
        <at name="beanclass">com.jeta.forms.components.label.JETALabel</at>
112
        <at name="beanproperties">
113
         <object classname="com.jeta.forms.store.memento.PropertiesMemento">
114
          <at name="classname">com.jeta.forms.components.label.JETALabel</at>
115
          <at name="properties">
116
           <object classname="com.jeta.forms.store.support.PropertyMap">
117
            <at name="border">
118
             <object classname="com.jeta.forms.store.properties.CompoundBorderProperty">
119
              <super classname="com.jeta.forms.store.properties.BorderProperty">
120
               <at name="name">border</at>
121
              </super>
122
              <at name="borders">
123
               <object classname="java.util.LinkedList">
124
                <item >
125
                 <at name="value">
126
                  <object classname="com.jeta.forms.store.properties.DefaultBorderProperty">
127
                   <super classname="com.jeta.forms.store.properties.BorderProperty">
128
                    <at name="name">border</at>
129
                   </super>
130
                  </object>
131
                 </at>
132
                </item>
133
               </object>
134
              </at>
135
             </object>
136
            </at>
137
            <at name="name">lblRealm</at>
138
            <at name="width">102</at>
139
            <at name="text">_Realm</at>
140
            <at name="fill">
141
             <object classname="com.jeta.forms.store.properties.effects.PaintProperty">
142
              <at name="name">fill</at>
143
             </object>
144
            </at>
145
            <at name="height">16</at>
146
           </object>
147
          </at>
148
         </object>
149
        </at>
150
       </object>
151
      </at>
152
     </item>
153
     <item >
154
      <at name="value">
155
       <object classname="com.jeta.forms.store.memento.BeanMemento">
156
        <super classname="com.jeta.forms.store.memento.ComponentMemento">
157
         <at name="cellconstraints">
158
          <object classname="com.jeta.forms.store.memento.CellConstraintsMemento">
159
           <at name="column">2</at>
160
           <at name="row">10</at>
161
           <at name="colspan">1</at>
162
           <at name="rowspan">1</at>
163
           <at name="halign">default</at>
164
           <at name="valign">default</at>
165
           <at name="insets" object="insets">0,0,0,0</at>
166
          </object>
167
         </at>
168
         <at name="componentclass">com.jeta.forms.gui.form.StandardComponent</at>
169
        </super>
170
        <at name="jetabeanclass">com.jeta.forms.gui.beans.JETABean</at>
171
        <at name="beanclass">com.jeta.forms.components.label.JETALabel</at>
172
        <at name="beanproperties">
173
         <object classname="com.jeta.forms.store.memento.PropertiesMemento">
174
          <at name="classname">com.jeta.forms.components.label.JETALabel</at>
175
          <at name="properties">
176
           <object classname="com.jeta.forms.store.support.PropertyMap">
177
            <at name="border">
178
             <object classname="com.jeta.forms.store.properties.CompoundBorderProperty">
179
              <super classname="com.jeta.forms.store.properties.BorderProperty">
180
               <at name="name">border</at>
181
              </super>
182
              <at name="borders">
183
               <object classname="java.util.LinkedList">
184
                <item >
185
                 <at name="value">
186
                  <object classname="com.jeta.forms.store.properties.DefaultBorderProperty">
187
                   <super classname="com.jeta.forms.store.properties.BorderProperty">
188
                    <at name="name">border</at>
189
                   </super>
190
                  </object>
191
                 </at>
192
                </item>
193
               </object>
194
              </at>
195
             </object>
196
            </at>
197
            <at name="name">lblLocalPort</at>
198
            <at name="width">102</at>
199
            <at name="text">_Local_port</at>
200
            <at name="fill">
201
             <object classname="com.jeta.forms.store.properties.effects.PaintProperty">
202
              <at name="name">fill</at>
203
             </object>
204
            </at>
205
            <at name="height">16</at>
206
           </object>
207
          </at>
208
         </object>
209
        </at>
210
       </object>
211
      </at>
212
     </item>
213
     <item >
214
      <at name="value">
215
       <object classname="com.jeta.forms.store.memento.BeanMemento">
216
        <super classname="com.jeta.forms.store.memento.ComponentMemento">
217
         <at name="cellconstraints">
218
          <object classname="com.jeta.forms.store.memento.CellConstraintsMemento">
219
           <at name="column">4</at>
220
           <at name="row">8</at>
221
           <at name="colspan">1</at>
222
           <at name="rowspan">1</at>
223
           <at name="halign">default</at>
224
           <at name="valign">default</at>
225
           <at name="insets" object="insets">0,0,0,0</at>
226
          </object>
227
         </at>
228
         <at name="componentclass">com.jeta.forms.gui.form.StandardComponent</at>
229
        </super>
230
        <at name="jetabeanclass">com.jeta.forms.gui.beans.JETABean</at>
231
        <at name="beanclass">javax.swing.JTextField</at>
232
        <at name="beanproperties">
233
         <object classname="com.jeta.forms.store.memento.PropertiesMemento">
234
          <at name="classname">javax.swing.JTextField</at>
235
          <at name="properties">
236
           <object classname="com.jeta.forms.store.support.PropertyMap">
237
            <at name="border">
238
             <object classname="com.jeta.forms.store.properties.CompoundBorderProperty">
239
              <super classname="com.jeta.forms.store.properties.BorderProperty">
240
               <at name="name">border</at>
241
              </super>
242
              <at name="borders">
243
               <object classname="java.util.LinkedList">
244
                <item >
245
                 <at name="value">
246
                  <object classname="com.jeta.forms.store.properties.DefaultBorderProperty">
247
                   <super classname="com.jeta.forms.store.properties.BorderProperty">
248
                    <at name="name">border</at>
249
                   </super>
250
                  </object>
251
                 </at>
252
                </item>
253
               </object>
254
              </at>
255
             </object>
256
            </at>
257
            <at name="name">txtRealm</at>
258
            <at name="width">812</at>
259
            <at name="height">22</at>
260
           </object>
261
          </at>
262
         </object>
263
        </at>
264
       </object>
265
      </at>
266
     </item>
267
     <item >
268
      <at name="value">
269
       <object classname="com.jeta.forms.store.memento.BeanMemento">
270
        <super classname="com.jeta.forms.store.memento.ComponentMemento">
271
         <at name="cellconstraints">
272
          <object classname="com.jeta.forms.store.memento.CellConstraintsMemento">
273
           <at name="column">4</at>
274
           <at name="row">10</at>
275
           <at name="colspan">1</at>
276
           <at name="rowspan">1</at>
277
           <at name="halign">default</at>
278
           <at name="valign">default</at>
279
           <at name="insets" object="insets">0,0,0,0</at>
280
          </object>
281
         </at>
282
         <at name="componentclass">com.jeta.forms.gui.form.StandardComponent</at>
283
        </super>
284
        <at name="jetabeanclass">com.jeta.forms.gui.beans.JETABean</at>
285
        <at name="beanclass">javax.swing.JTextField</at>
286
        <at name="beanproperties">
287
         <object classname="com.jeta.forms.store.memento.PropertiesMemento">
288
          <at name="classname">javax.swing.JTextField</at>
289
          <at name="properties">
290
           <object classname="com.jeta.forms.store.support.PropertyMap">
291
            <at name="border">
292
             <object classname="com.jeta.forms.store.properties.CompoundBorderProperty">
293
              <super classname="com.jeta.forms.store.properties.BorderProperty">
294
               <at name="name">border</at>
295
              </super>
296
              <at name="borders">
297
               <object classname="java.util.LinkedList">
298
                <item >
299
                 <at name="value">
300
                  <object classname="com.jeta.forms.store.properties.DefaultBorderProperty">
301
                   <super classname="com.jeta.forms.store.properties.BorderProperty">
302
                    <at name="name">border</at>
303
                   </super>
304
                  </object>
305
                 </at>
306
                </item>
307
               </object>
308
              </at>
309
             </object>
310
            </at>
311
            <at name="name">txtLocalPort</at>
312
            <at name="width">812</at>
313
            <at name="height">22</at>
314
           </object>
315
          </at>
316
         </object>
317
        </at>
318
       </object>
319
      </at>
320
     </item>
321
     <item >
322
      <at name="value">
323
       <object classname="com.jeta.forms.store.memento.BeanMemento">
324
        <super classname="com.jeta.forms.store.memento.ComponentMemento">
325
         <at name="cellconstraints">
326
          <object classname="com.jeta.forms.store.memento.CellConstraintsMemento">
327
           <at name="column">2</at>
328
           <at name="row">4</at>
329
           <at name="colspan">1</at>
330
           <at name="rowspan">1</at>
331
           <at name="halign">default</at>
332
           <at name="valign">default</at>
333
           <at name="insets" object="insets">0,0,0,0</at>
334
          </object>
335
         </at>
336
         <at name="componentclass">com.jeta.forms.gui.form.StandardComponent</at>
337
        </super>
338
        <at name="jetabeanclass">com.jeta.forms.gui.beans.JETABean</at>
339
        <at name="beanclass">com.jeta.forms.components.label.JETALabel</at>
340
        <at name="beanproperties">
341
         <object classname="com.jeta.forms.store.memento.PropertiesMemento">
342
          <at name="classname">com.jeta.forms.components.label.JETALabel</at>
343
          <at name="properties">
344
           <object classname="com.jeta.forms.store.support.PropertyMap">
345
            <at name="border">
346
             <object classname="com.jeta.forms.store.properties.CompoundBorderProperty">
347
              <super classname="com.jeta.forms.store.properties.BorderProperty">
348
               <at name="name">border</at>
349
              </super>
350
              <at name="borders">
351
               <object classname="java.util.LinkedList">
352
                <item >
353
                 <at name="value">
354
                  <object classname="com.jeta.forms.store.properties.DefaultBorderProperty">
355
                   <super classname="com.jeta.forms.store.properties.BorderProperty">
356
                    <at name="name">border</at>
357
                   </super>
358
                  </object>
359
                 </at>
360
                </item>
361
               </object>
362
              </at>
363
             </object>
364
            </at>
365
            <at name="name">lblUrlOnline</at>
366
            <at name="width">102</at>
367
            <at name="text">_URL_gvSIG_Online</at>
368
            <at name="fill">
369
             <object classname="com.jeta.forms.store.properties.effects.PaintProperty">
370
              <at name="name">fill</at>
371
             </object>
372
            </at>
373
            <at name="height">16</at>
374
           </object>
375
          </at>
376
         </object>
377
        </at>
378
       </object>
379
      </at>
380
     </item>
381
     <item >
382
      <at name="value">
383
       <object classname="com.jeta.forms.store.memento.BeanMemento">
384
        <super classname="com.jeta.forms.store.memento.ComponentMemento">
385
         <at name="cellconstraints">
386
          <object classname="com.jeta.forms.store.memento.CellConstraintsMemento">
387
           <at name="column">4</at>
388
           <at name="row">4</at>
389
           <at name="colspan">1</at>
390
           <at name="rowspan">1</at>
391
           <at name="halign">default</at>
392
           <at name="valign">default</at>
393
           <at name="insets" object="insets">0,0,0,0</at>
394
          </object>
395
         </at>
396
         <at name="componentclass">com.jeta.forms.gui.form.StandardComponent</at>
397
        </super>
398
        <at name="jetabeanclass">com.jeta.forms.gui.beans.JETABean</at>
399
        <at name="beanclass">javax.swing.JTextField</at>
400
        <at name="beanproperties">
401
         <object classname="com.jeta.forms.store.memento.PropertiesMemento">
402
          <at name="classname">javax.swing.JTextField</at>
403
          <at name="properties">
404
           <object classname="com.jeta.forms.store.support.PropertyMap">
405
            <at name="border">
406
             <object classname="com.jeta.forms.store.properties.CompoundBorderProperty">
407
              <super classname="com.jeta.forms.store.properties.BorderProperty">
408
               <at name="name">border</at>
409
              </super>
410
              <at name="borders">
411
               <object classname="java.util.LinkedList">
412
                <item >
413
                 <at name="value">
414
                  <object classname="com.jeta.forms.store.properties.DefaultBorderProperty">
415
                   <super classname="com.jeta.forms.store.properties.BorderProperty">
416
                    <at name="name">border</at>
417
                   </super>
418
                  </object>
419
                 </at>
420
                </item>
421
               </object>
422
              </at>
423
             </object>
424
            </at>
425
            <at name="name">txtUrlOnline</at>
426
            <at name="width">812</at>
427
            <at name="height">22</at>
428
           </object>
429
          </at>
430
         </object>
431
        </at>
432
       </object>
433
      </at>
434
     </item>
435
     <item >
436
      <at name="value">
437
       <object classname="com.jeta.forms.store.memento.FormMemento">
438
        <super classname="com.jeta.forms.store.memento.ComponentMemento">
439
         <at name="cellconstraints">
440
          <object classname="com.jeta.forms.store.memento.CellConstraintsMemento">
441
           <at name="column">4</at>
442
           <at name="row">2</at>
443
           <at name="colspan">1</at>
444
           <at name="rowspan">1</at>
445
           <at name="halign">right</at>
446
           <at name="valign">default</at>
447
           <at name="insets" object="insets">0,0,0,0</at>
448
          </object>
449
         </at>
450
         <at name="componentclass">com.jeta.forms.gui.form.FormComponent</at>
451
        </super>
452
        <at name="id">embedded.612518715</at>
453
        <at name="rowspecs">CENTER:DEFAULT:NONE</at>
454
        <at name="colspecs">FILL:DEFAULT:NONE,FILL:4DLU:NONE,FILL:DEFAULT:NONE,FILL:4DLU:NONE,FILL:DEFAULT:NONE</at>
455
        <at name="components">
456
         <object classname="java.util.LinkedList">
457
          <item >
458
           <at name="value">
459
            <object classname="com.jeta.forms.store.memento.BeanMemento">
460
             <super classname="com.jeta.forms.store.memento.ComponentMemento">
461
              <at name="cellconstraints">
462
               <object classname="com.jeta.forms.store.memento.CellConstraintsMemento">
463
                <at name="column">3</at>
464
                <at name="row">1</at>
465
                <at name="colspan">1</at>
466
                <at name="rowspan">1</at>
467
                <at name="halign">default</at>
468
                <at name="valign">default</at>
469
                <at name="insets" object="insets">0,0,0,0</at>
470
               </object>
471
              </at>
472
              <at name="componentclass">com.jeta.forms.gui.form.StandardComponent</at>
473
             </super>
474
             <at name="jetabeanclass">com.jeta.forms.gui.beans.JETABean</at>
475
             <at name="beanclass">javax.swing.JButton</at>
476
             <at name="beanproperties">
477
              <object classname="com.jeta.forms.store.memento.PropertiesMemento">
478
               <at name="classname">javax.swing.JButton</at>
479
               <at name="properties">
480
                <object classname="com.jeta.forms.store.support.PropertyMap">
481
                 <at name="border">
482
                  <object classname="com.jeta.forms.store.properties.CompoundBorderProperty">
483
                   <super classname="com.jeta.forms.store.properties.BorderProperty">
484
                    <at name="name">border</at>
485
                   </super>
486
                   <at name="borders">
487
                    <object classname="java.util.LinkedList">
488
                     <item >
489
                      <at name="value">
490
                       <object classname="com.jeta.forms.store.properties.EmptyBorderProperty">
491
                        <super classname="com.jeta.forms.store.properties.BorderProperty">
492
                         <at name="name">border</at>
493
                        </super>
494
                        <at name="top">2</at>
495
                        <at name="left">2</at>
496
                        <at name="bottom">2</at>
497
                        <at name="right">2</at>
498
                       </object>
499
                      </at>
500
                     </item>
501
                    </object>
502
                   </at>
503
                  </object>
504
                 </at>
505
                 <at name="actionCommand">...</at>
506
                 <at name="name">btnConfigKeycloakHistory</at>
507
                 <at name="width">13</at>
508
                 <at name="text">...</at>
509
                 <at name="height">20</at>
510
                </object>
511
               </at>
512
              </object>
513
             </at>
514
            </object>
515
           </at>
516
          </item>
517
          <item >
518
           <at name="value">
519
            <object classname="com.jeta.forms.store.memento.BeanMemento">
520
             <super classname="com.jeta.forms.store.memento.ComponentMemento">
521
              <at name="cellconstraints">
522
               <object classname="com.jeta.forms.store.memento.CellConstraintsMemento">
523
                <at name="column">5</at>
524
                <at name="row">1</at>
525
                <at name="colspan">1</at>
526
                <at name="rowspan">1</at>
527
                <at name="halign">default</at>
528
                <at name="valign">default</at>
529
                <at name="insets" object="insets">0,0,0,0</at>
530
               </object>
531
              </at>
532
              <at name="componentclass">com.jeta.forms.gui.form.StandardComponent</at>
533
             </super>
534
             <at name="jetabeanclass">com.jeta.forms.gui.beans.JETABean</at>
535
             <at name="beanclass">javax.swing.JButton</at>
536
             <at name="beanproperties">
537
              <object classname="com.jeta.forms.store.memento.PropertiesMemento">
538
               <at name="classname">javax.swing.JButton</at>
539
               <at name="properties">
540
                <object classname="com.jeta.forms.store.support.PropertyMap">
541
                 <at name="border">
542
                  <object classname="com.jeta.forms.store.properties.CompoundBorderProperty">
543
                   <super classname="com.jeta.forms.store.properties.BorderProperty">
544
                    <at name="name">border</at>
545
                   </super>
546
                   <at name="borders">
547
                    <object classname="java.util.LinkedList">
548
                     <item >
549
                      <at name="value">
550
                       <object classname="com.jeta.forms.store.properties.EmptyBorderProperty">
551
                        <super classname="com.jeta.forms.store.properties.BorderProperty">
552
                         <at name="name">border</at>
553
                        </super>
554
                        <at name="top">2</at>
555
                        <at name="left">2</at>
556
                        <at name="bottom">2</at>
557
                        <at name="right">2</at>
558
                       </object>
559
                      </at>
560
                     </item>
561
                    </object>
562
                   </at>
563
                  </object>
564
                 </at>
565
                 <at name="actionCommand">...</at>
566
                 <at name="name">btnConfigKeycloakBookmarks</at>
567
                 <at name="width">13</at>
568
                 <at name="text">...</at>
569
                 <at name="height">20</at>
570
                </object>
571
               </at>
572
              </object>
573
             </at>
574
            </object>
575
           </at>
576
          </item>
577
         </object>
578
        </at>
579
        <at name="properties">
580
         <object classname="com.jeta.forms.store.memento.PropertiesMemento">
581
          <at name="classname">com.jeta.forms.gui.form.GridView</at>
582
          <at name="properties">
583
           <object classname="com.jeta.forms.store.support.PropertyMap">
584
            <at name="border">
585
             <object classname="com.jeta.forms.store.properties.CompoundBorderProperty">
586
              <super classname="com.jeta.forms.store.properties.BorderProperty">
587
               <at name="name">border</at>
588
              </super>
589
              <at name="borders">
590
               <object classname="java.util.LinkedList"/>
591
              </at>
592
             </object>
593
            </at>
594
            <at name="name"></at>
595
            <at name="fill">
596
             <object classname="com.jeta.forms.store.properties.effects.PaintProperty">
597
              <at name="name">fill</at>
598
             </object>
599
            </at>
600
            <at name="scollBars">
601
             <object classname="com.jeta.forms.store.properties.ScrollBarsProperty">
602
              <at name="name">scollBars</at>
603
              <at name="verticalpolicy">21</at>
604
              <at name="horizontalpolicy">31</at>
605
              <at name="border">
606
               <object classname="com.jeta.forms.store.properties.CompoundBorderProperty">
... This diff was truncated because it exceeds the maximum size that can be displayed.

Also available in: Unified diff