source: josm/trunk/src/org/openstreetmap/josm/gui/JosmUserIdentityManager.java@ 12718

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

see #15182 - move OAuthAccessTokenHolder from gui.preferences.server to data.oauth

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