source: josm/trunk/src/org/openstreetmap/josm/data/UserIdentityManager.java@ 12813

Last change on this file since 12813 was 12744, checked in by Don-vip, 7 years ago

see #15229 - see #15182 - remove GUI dependence of PlatformHookWindows - move workaround to MainApplication

  • Property svn:eol-style set to native
File size: 11.9 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.data;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.text.MessageFormat;
7
8import org.openstreetmap.josm.Main;
9import org.openstreetmap.josm.data.Preferences.PreferenceChangeEvent;
10import org.openstreetmap.josm.data.Preferences.PreferenceChangedListener;
11import org.openstreetmap.josm.data.oauth.OAuthAccessTokenHolder;
12import org.openstreetmap.josm.data.osm.User;
13import org.openstreetmap.josm.data.osm.UserInfo;
14import org.openstreetmap.josm.data.preferences.StringSetting;
15import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
16import org.openstreetmap.josm.io.OnlineResource;
17import org.openstreetmap.josm.io.OsmApi;
18import org.openstreetmap.josm.io.OsmServerUserInfoReader;
19import org.openstreetmap.josm.io.OsmTransferException;
20import org.openstreetmap.josm.io.auth.CredentialsManager;
21import org.openstreetmap.josm.tools.CheckParameterUtil;
22import org.openstreetmap.josm.tools.JosmRuntimeException;
23import org.openstreetmap.josm.tools.Logging;
24
25/**
26 * UserIdentityManager is a global object which keeps track of what JOSM knows about
27 * the identity of the current user.
28 *
29 * JOSM can be operated anonymously provided the current user never invokes an operation
30 * on the OSM server which required authentication. In this case JOSM neither knows
31 * the user name of the OSM account of the current user nor its unique id. Perhaps the
32 * user doesn't have one.
33 *
34 * If the current user supplies a user name and a password in the JOSM preferences JOSM
35 * can partially identify the user.
36 *
37 * The current user is fully identified if JOSM knows both the user name and the unique
38 * id of the users OSM account. The latter is retrieved from the OSM server with a
39 * <tt>GET /api/0.6/user/details</tt> request, submitted with the user name and password
40 * of the current user.
41 *
42 * The global UserIdentityManager listens to {@link PreferenceChangeEvent}s and keeps track
43 * of what the current JOSM instance knows about the current user. Other subsystems can
44 * let the global UserIdentityManager know in case they fully identify the current user, see
45 * {@link #setFullyIdentified}.
46 *
47 * The information kept by the UserIdentityManager can be used to
48 * <ul>
49 * <li>safely query changesets owned by the current user based on its user id, not on its user name</li>
50 * <li>safely search for objects last touched by the current user based on its user id, not on its user name</li>
51 * </ul>
52 * @since 12743 (renamed from {@code org.openstreetmap.josm.gui.JosmUserIdentityManager})
53 * @since 2689 (creation)
54 */
55public final class UserIdentityManager implements PreferenceChangedListener {
56
57 private static UserIdentityManager instance;
58
59 /**
60 * Replies the unique instance of the JOSM user identity manager
61 *
62 * @return the unique instance of the JOSM user identity manager
63 */
64 public static synchronized UserIdentityManager getInstance() {
65 if (instance == null) {
66 instance = new UserIdentityManager();
67 if (OsmApi.isUsingOAuth() && OAuthAccessTokenHolder.getInstance().containsAccessToken() &&
68 !Main.isOffline(OnlineResource.OSM_API)) {
69 try {
70 instance.initFromOAuth();
71 } catch (JosmRuntimeException | IllegalArgumentException | IllegalStateException e) {
72 Logging.error(e);
73 // Fall back to preferences if OAuth identification fails for any reason
74 instance.initFromPreferences();
75 }
76 } else {
77 instance.initFromPreferences();
78 }
79 Main.pref.addPreferenceChangeListener(instance);
80 }
81 return instance;
82 }
83
84 private String userName;
85 private UserInfo userInfo;
86 private boolean accessTokenKeyChanged;
87 private boolean accessTokenSecretChanged;
88
89 private UserIdentityManager() {
90 }
91
92 /**
93 * Remembers the fact that the current JOSM user is anonymous.
94 */
95 public void setAnonymous() {
96 userName = null;
97 userInfo = null;
98 }
99
100 /**
101 * Remembers the fact that the current JOSM user is partially identified
102 * by the user name of its OSM account.
103 *
104 * @param userName the user name. Must not be null. Must not be empty (whitespace only).
105 * @throws IllegalArgumentException if userName is null
106 * @throws IllegalArgumentException if userName is empty
107 */
108 public void setPartiallyIdentified(String userName) {
109 CheckParameterUtil.ensureParameterNotNull(userName, "userName");
110 String trimmedUserName = userName.trim();
111 if (trimmedUserName.isEmpty())
112 throw new IllegalArgumentException(
113 MessageFormat.format("Expected non-empty value for parameter ''{0}'', got ''{1}''", "userName", userName));
114 this.userName = trimmedUserName;
115 userInfo = null;
116 }
117
118 /**
119 * Remembers the fact that the current JOSM user is fully identified with a
120 * verified pair of user name and user id.
121 *
122 * @param userName the user name. Must not be null. Must not be empty.
123 * @param userInfo additional information about the user, retrieved from the OSM server and including the user id
124 * @throws IllegalArgumentException if userName is null
125 * @throws IllegalArgumentException if userName is empty
126 * @throws IllegalArgumentException if userInfo is null
127 */
128 public void setFullyIdentified(String userName, UserInfo userInfo) {
129 CheckParameterUtil.ensureParameterNotNull(userName, "userName");
130 String trimmedUserName = userName.trim();
131 if (trimmedUserName.isEmpty())
132 throw new IllegalArgumentException(tr("Expected non-empty value for parameter ''{0}'', got ''{1}''", "userName", userName));
133 CheckParameterUtil.ensureParameterNotNull(userInfo, "userInfo");
134 this.userName = trimmedUserName;
135 this.userInfo = userInfo;
136 }
137
138 /**
139 * Replies true if the current JOSM user is anonymous.
140 *
141 * @return {@code true} if the current user is anonymous.
142 */
143 public boolean isAnonymous() {
144 return userName == null && userInfo == null;
145 }
146
147 /**
148 * Replies true if the current JOSM user is partially identified.
149 *
150 * @return true if the current JOSM user is partially identified.
151 */
152 public boolean isPartiallyIdentified() {
153 return userName != null && userInfo == null;
154 }
155
156 /**
157 * Replies true if the current JOSM user is fully identified.
158 *
159 * @return true if the current JOSM user is fully identified.
160 */
161 public boolean isFullyIdentified() {
162 return userName != null && userInfo != null;
163 }
164
165 /**
166 * Replies the user name of the current JOSM user. null, if {@link #isAnonymous()} is true.
167 *
168 * @return the user name of the current JOSM user
169 */
170 public String getUserName() {
171 return userName;
172 }
173
174 /**
175 * Replies the user id of the current JOSM user. 0, if {@link #isAnonymous()} or
176 * {@link #isPartiallyIdentified()} is true.
177 *
178 * @return the user id of the current JOSM user
179 */
180 public int getUserId() {
181 if (userInfo == null) return 0;
182 return userInfo.getId();
183 }
184
185 /**
186 * Replies verified additional information about the current user if the user is
187 * {@link #isFullyIdentified()}.
188 *
189 * @return verified additional information about the current user
190 */
191 public UserInfo getUserInfo() {
192 return userInfo;
193 }
194
195 /**
196 * Returns the identity as a {@link User} object
197 *
198 * @return the identity as user, or {@link User#getAnonymous()} if {@link #isAnonymous()}
199 */
200 public User asUser() {
201 return isAnonymous() ? User.getAnonymous() : User.createOsmUser(userInfo != null ? userInfo.getId() : 0, userName);
202 }
203
204 /**
205 * Initializes the user identity manager from Basic Authentication values in the {@link org.openstreetmap.josm.data.Preferences}
206 * This method should be called if {@code osm-server.auth-method} is set to {@code basic}.
207 * @see #initFromOAuth
208 */
209 public void initFromPreferences() {
210 String userName = CredentialsManager.getInstance().getUsername();
211 if (isAnonymous()) {
212 if (userName != null && !userName.trim().isEmpty()) {
213 setPartiallyIdentified(userName);
214 }
215 } else {
216 if (userName != null && !userName.equals(this.userName)) {
217 setPartiallyIdentified(userName);
218 }
219 // else: same name in the preferences as JOSM already knows about.
220 // keep the state, be it partially or fully identified
221 }
222 }
223
224 /**
225 * Initializes the user identity manager from OAuth request of user details.
226 * This method should be called if {@code osm-server.auth-method} is set to {@code oauth}.
227 * @see #initFromPreferences
228 * @since 5434
229 */
230 public void initFromOAuth() {
231 try {
232 UserInfo info = new OsmServerUserInfoReader().fetchUserInfo(NullProgressMonitor.INSTANCE);
233 setFullyIdentified(info.getDisplayName(), info);
234 } catch (IllegalArgumentException | OsmTransferException e) {
235 Logging.error(e);
236 }
237 }
238
239 /**
240 * Replies true if the user with name <code>username</code> is the current user
241 *
242 * @param username the user name
243 * @return true if the user with name <code>username</code> is the current user
244 */
245 public boolean isCurrentUser(String username) {
246 return this.userName != null && this.userName.equals(username);
247 }
248
249 /**
250 * Replies true if the current user is {@link #isFullyIdentified() fully identified} and the {@link #getUserId() user ids} match,
251 * or if the current user is not {@link #isFullyIdentified() fully identified} and the {@link #getUserName() user names} match.
252 *
253 * @param user the user to test
254 * @return true if given user is the current user
255 */
256 public boolean isCurrentUser(User user) {
257 if (user == null) {
258 return false;
259 } else if (isFullyIdentified()) {
260 return getUserId() == user.getId();
261 } else {
262 return isCurrentUser(user.getName());
263 }
264 }
265
266 /* ------------------------------------------------------------------- */
267 /* interface PreferenceChangeListener */
268 /* ------------------------------------------------------------------- */
269 @Override
270 public void preferenceChanged(PreferenceChangeEvent evt) {
271 switch (evt.getKey()) {
272 case "osm-server.username":
273 String newUserName = null;
274 if (evt.getNewValue() instanceof StringSetting) {
275 newUserName = ((StringSetting) evt.getNewValue()).getValue();
276 }
277 if (newUserName == null || newUserName.trim().isEmpty()) {
278 setAnonymous();
279 } else {
280 if (!newUserName.equals(userName)) {
281 setPartiallyIdentified(newUserName);
282 }
283 }
284 return;
285 case "osm-server.url":
286 String newUrl = null;
287 if (evt.getNewValue() instanceof StringSetting) {
288 newUrl = ((StringSetting) evt.getNewValue()).getValue();
289 }
290 if (newUrl == null || newUrl.trim().isEmpty()) {
291 setAnonymous();
292 } else if (isFullyIdentified()) {
293 setPartiallyIdentified(getUserName());
294 }
295 break;
296 case "oauth.access-token.key":
297 accessTokenKeyChanged = true;
298 break;
299 case "oauth.access-token.secret":
300 accessTokenSecretChanged = true;
301 break;
302 default: // Do nothing
303 }
304
305 if (accessTokenKeyChanged && accessTokenSecretChanged) {
306 accessTokenKeyChanged = false;
307 accessTokenSecretChanged = false;
308 if (OsmApi.isUsingOAuth()) {
309 getInstance().initFromOAuth();
310 }
311 }
312 }
313}
Note: See TracBrowser for help on using the repository browser.