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

Last change on this file since 13608 was 13493, checked in by Don-vip, 6 years ago

see #11924, see #15560, see #16048 - tt HTML tag is deprecated in HTML5: use code instead

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