001package com.box.sdk;
002
003import static com.box.sdk.BinaryBodyUtils.writeStream;
004import static com.box.sdk.http.HttpMethod.DELETE;
005import static com.box.sdk.internal.utils.JsonUtils.addIfNotNull;
006
007import com.eclipsesource.json.Json;
008import com.eclipsesource.json.JsonArray;
009import com.eclipsesource.json.JsonObject;
010import com.eclipsesource.json.JsonValue;
011import java.io.ByteArrayInputStream;
012import java.io.ByteArrayOutputStream;
013import java.io.File;
014import java.io.FileInputStream;
015import java.io.FileNotFoundException;
016import java.io.IOException;
017import java.io.InputStream;
018import java.io.OutputStream;
019import java.net.URL;
020import java.util.ArrayList;
021import java.util.Collection;
022import java.util.HashMap;
023import java.util.List;
024import java.util.Map;
025import java.util.Optional;
026
027/**
028 * Represents a Box user account.
029 *
030 * <p>Unless otherwise noted, the methods in this class can throw an unchecked {@link BoxAPIException} (unchecked
031 * meaning that the compiler won't force you to handle it) if an error occurs. If you wish to implement custom error
032 * handling for errors related to the Box REST API, you should capture this exception explicitly.</p>
033 */
034@BoxResourceType("user")
035public class BoxUser extends BoxCollaborator {
036
037    /**
038     * An array of all possible file fields that can be requested when calling {@link #getInfo(String...)}.
039     */
040    public static final String[] ALL_FIELDS = {"type", "id", "name", "login", "created_at", "modified_at", "role",
041        "language", "timezone", "space_amount", "space_used", "max_upload_size", "tracking_codes",
042        "can_see_managed_users", "is_sync_enabled", "is_external_collab_restricted", "status", "job_title", "phone",
043        "address", "avatar_url", "is_exempt_from_device_limits", "is_exempt_from_login_verification", "enterprise",
044        "my_tags", "hostname", "is_platform_access_only", "external_app_user_id", "notification_email"};
045
046    /**
047     * User URL Template.
048     */
049    public static final URLTemplate USER_URL_TEMPLATE = new URLTemplate("users/%s");
050    /**
051     * Get Me URL Template.
052     */
053    public static final URLTemplate GET_ME_URL = new URLTemplate("users/me");
054    /**
055     * Users URL Template.
056     */
057    public static final URLTemplate USERS_URL_TEMPLATE = new URLTemplate("users");
058    /**
059     * User Memberships URL Template.
060     */
061    public static final URLTemplate USER_MEMBERSHIPS_URL_TEMPLATE = new URLTemplate("users/%s/memberships");
062    /**
063     * E-Mail Alias URL Template.
064     */
065    public static final URLTemplate EMAIL_ALIAS_URL_TEMPLATE = new URLTemplate("users/%s/email_aliases/%s");
066    /**
067     * E-Mail Aliases URL Template.
068     */
069    public static final URLTemplate EMAIL_ALIASES_URL_TEMPLATE = new URLTemplate("users/%s/email_aliases");
070    /**
071     * Move Folder To User Template.
072     */
073    public static final URLTemplate MOVE_FOLDER_TO_USER_TEMPLATE = new URLTemplate("users/%s/folders/%s");
074    /**
075     * User Avatar Template.
076     */
077    public static final URLTemplate USER_AVATAR_TEMPLATE = new URLTemplate("users/%s/avatar");
078
079    /**
080     * Constructs a BoxUser for a user with a given ID.
081     *
082     * @param api the API connection to be used by the user.
083     * @param id  the ID of the user.
084     */
085    public BoxUser(BoxAPIConnection api, String id) {
086        super(api, id);
087    }
088
089    /**
090     * Provisions a new app user in an enterprise using Box Developer Edition.
091     *
092     * @param api  the API connection to be used by the created user.
093     * @param name the name of the user.
094     * @param fields the fields to retrieve. Leave this out for the standard fields.
095     * @return the created user's info.
096     */
097    public static BoxUser.Info createAppUser(BoxAPIConnection api, String name, String... fields) {
098        return createAppUser(api, name, new CreateUserParams(), fields);
099    }
100
101    /**
102     * Provisions a new app user in an enterprise with additional user information using Box Developer Edition.
103     *
104     * @param api    the API connection to be used by the created user.
105     * @param name   the name of the user.
106     * @param params additional user information.
107     * @param fields the fields to retrieve. Leave this out for the standard fields.
108     * @return the created user's info.
109     */
110    public static BoxUser.Info createAppUser(BoxAPIConnection api, String name,
111                                             CreateUserParams params, String... fields) {
112
113        params.setIsPlatformAccessOnly(true);
114        return createEnterpriseUser(api, null, name, params, fields);
115    }
116
117    /**
118     * Provisions a new user in an enterprise.
119     *
120     * @param api   the API connection to be used by the created user.
121     * @param login the email address the user will use to login.
122     * @param name  the name of the user.
123     * @param fields the fields to retrieve. Leave this out for the standard fields.
124     * @return the created user's info.
125     */
126    public static BoxUser.Info createEnterpriseUser(BoxAPIConnection api, String login, String name, String... fields) {
127        return createEnterpriseUser(api, login, name, null, fields);
128    }
129
130    /**
131     * Provisions a new user in an enterprise with additional user information.
132     *
133     * @param api    the API connection to be used by the created user.
134     * @param login  the email address the user will use to login.
135     * @param name   the name of the user.
136     * @param params additional user information.
137     * @param fields the fields to retrieve. Leave this out for the standard fields.
138     * @return the created user's info.
139     */
140    public static BoxUser.Info createEnterpriseUser(BoxAPIConnection api, String login, String name,
141                                                    CreateUserParams params, String... fields) {
142
143        JsonObject requestJSON = new JsonObject();
144        requestJSON.add("login", login);
145        requestJSON.add("name", name);
146
147        if (params != null) {
148            if (params.getRole() != null) {
149                requestJSON.add("role", params.getRole().toJSONValue());
150            }
151
152            if (params.getStatus() != null) {
153                requestJSON.add("status", params.getStatus().toJSONValue());
154            }
155
156            if (params.getTrackingCodes() != null) {
157                requestJSON.add("tracking_codes", toTrackingCodesJson(params.getTrackingCodes()));
158            }
159
160            addIfNotNull(requestJSON, "language", params.getLanguage());
161            addIfNotNull(requestJSON, "is_sync_enabled", params.getIsSyncEnabled());
162            addIfNotNull(requestJSON, "job_title", params.getJobTitle());
163            addIfNotNull(requestJSON, "phone", params.getPhone());
164            addIfNotNull(requestJSON, "address", params.getAddress());
165            addIfNotNull(requestJSON, "space_amount", params.getSpaceAmount());
166            addIfNotNull(requestJSON, "can_see_managed_users", params.getCanSeeManagedUsers());
167            addIfNotNull(requestJSON, "timezone", params.getTimezone());
168            addIfNotNull(requestJSON, "is_exempt_from_device_limits", params.getIsExemptFromDeviceLimits());
169            addIfNotNull(requestJSON, "is_exempt_from_login_verification", params.getIsExemptFromLoginVerification());
170            addIfNotNull(requestJSON, "is_platform_access_only", params.getIsPlatformAccessOnly());
171            addIfNotNull(requestJSON, "external_app_user_id", params.getExternalAppUserId());
172            addIfNotNull(requestJSON, "is_external_collab_restricted", params.getIsExternalCollabRestricted());
173        }
174
175        QueryStringBuilder queryString = new QueryStringBuilder();
176        if (fields.length > 0) {
177            queryString.appendParam("fields", fields);
178        }
179        URL url = USERS_URL_TEMPLATE.buildWithQuery(api.getBaseURL(), queryString.toString());
180        BoxJSONRequest request = new BoxJSONRequest(api, url, "POST");
181        request.setBody(requestJSON.toString());
182        try (BoxJSONResponse response = request.send()) {
183            JsonObject responseJSON = Json.parse(response.getJSON()).asObject();
184
185            BoxUser createdUser = new BoxUser(api, responseJSON.get("id").asString());
186            return createdUser.new Info(responseJSON);
187        }
188    }
189
190    /**
191     * Gets the current user.
192     *
193     * @param api the API connection of the current user.
194     * @return the current user.
195     */
196    public static BoxUser getCurrentUser(BoxAPIConnection api) {
197        URL url = GET_ME_URL.build(api.getBaseURL());
198        BoxJSONRequest request = new BoxJSONRequest(api, url, "GET");
199        try (BoxJSONResponse response = request.send()) {
200            JsonObject jsonObject = Json.parse(response.getJSON()).asObject();
201            return new BoxUser(api, jsonObject.get("id").asString());
202        }
203    }
204
205    /**
206     * Returns an iterable containing all the enterprise users.
207     *
208     * @param api the API connection to be used when retrieving the users.
209     * @return an iterable containing all the enterprise users.
210     */
211    public static Iterable<BoxUser.Info> getAllEnterpriseUsers(final BoxAPIConnection api) {
212        return getAllEnterpriseUsers(api, false, null);
213    }
214
215
216    /**
217     * Returns an iterable containing all the enterprise users. Uses marker based pagination.
218     *
219     * @param api       the API connection to be used when retrieving the users.
220     * @param usemarker Boolean that determines whether to use marker based pagination.
221     * @param marker    The marker at which the iterator will begin.
222     * @return an iterable containing all the enterprise users.
223     */
224    public static Iterable<BoxUser.Info> getAllEnterpriseUsers(final BoxAPIConnection api, final boolean usemarker,
225                                                               final String marker) {
226        return getUsersInfoForType(api, null, null, null, usemarker, marker);
227    }
228
229    /**
230     * Returns an iterable containing all the enterprise users that matches the filter and specifies which child fields
231     * to retrieve from the API.
232     *
233     * @param api        the API connection to be used when retrieving the users.
234     * @param filterTerm used to filter the results to only users starting with this string in either the name or the
235     *                   login. Can be null to not filter the results.
236     * @param fields     the fields to retrieve. Leave this out for the standard fields.
237     * @return an iterable containing all the enterprise users that matches the filter.
238     */
239    public static Iterable<BoxUser.Info> getAllEnterpriseUsers(final BoxAPIConnection api, final String filterTerm,
240                                                               final String... fields) {
241        return getUsersInfoForType(api, filterTerm, null, null, false, null, fields);
242    }
243
244    /**
245     * Returns an iterable containing all the enterprise users that matches the filter and specifies which child fields
246     * to retrieve from the API. Uses marker based pagination.
247     *
248     * @param api        the API connection to be used when retrieving the users.
249     * @param filterTerm used to filter the results to only users starting with this string in either the name or the
250     *                   login. Can be null to not filter the results.
251     * @param usemarker  Boolean that determines whether to use marker based pagination.
252     * @param marker     The marker at which the iterator will begin.
253     * @param fields     the fields to retrieve. Leave this out for the standard fields.
254     * @return an iterable containing all the enterprise users that matches the filter.
255     */
256    public static Iterable<BoxUser.Info> getAllEnterpriseUsers(
257        final BoxAPIConnection api,
258        final String filterTerm,
259        final boolean usemarker,
260        final String marker,
261        final String... fields
262    ) {
263        return getUsersInfoForType(api, filterTerm, null, null, usemarker, marker, fields);
264    }
265
266    /**
267     * Gets a limited set of information about an external user. (A user collaborating
268     * on content owned by the enterprise). Note: Only fields the user has permission to
269     * see will be returned with values. Other fields will return a value of null.
270     *
271     * @param api        the API connection to be used when retrieving the users.
272     * @param filterTerm used to filter the results to only users matching the given login.
273     *                   This does exact match only, so if no filter term is passed in, nothing
274     *                   will be returned.
275     * @param fields     the fields to retrieve. Leave this out for the standard fields.
276     * @return an iterable containing external users matching the given email
277     */
278    public static Iterable<BoxUser.Info> getExternalUsers(final BoxAPIConnection api, final String filterTerm,
279                                                          final String... fields) {
280        return getUsersInfoForType(api, filterTerm, "external", null, false, null, fields);
281    }
282
283    /**
284     * Gets a limited set of information about an external user. (A user collaborating
285     * on content owned by the enterprise). Note: Only fields the user has permission to
286     * see will be returned with values. Other fields will return a value of null. Uses marker based pagination.
287     *
288     * @param api        the API connection to be used when retrieving the users.
289     * @param filterTerm used to filter the results to only users matching the given login.
290     *                   This does exact match only, so if no filter term is passed in, nothing
291     *                   will be returned.
292     * @param usemarker  Boolean that determines whether to use marker based pagination.
293     * @param marker     The marker at which the iterator will begin.
294     * @param fields     the fields to retrieve. Leave this out for the standard fields.
295     * @return an iterable containing external users matching the given email
296     */
297    public static Iterable<BoxUser.Info> getExternalUsers(
298        final BoxAPIConnection api,
299        final String filterTerm,
300        final boolean usemarker,
301        final String marker,
302        final String... fields
303    ) {
304        return getUsersInfoForType(api, filterTerm, "external", null, usemarker, marker, fields);
305    }
306
307    /**
308     * Gets any managed users that match the filter term as well as any external users that
309     * match the filter term. For managed users it matches any users names or emails that
310     * start with the term. For external, it only does full match on email. This method
311     * is ideal to use in the case where you have a full email for a user and you don't
312     * know if they're managed or external.
313     *
314     * @param api        the API connection to be used when retrieving the users.
315     * @param filterTerm The filter term to lookup users by (login for external, login or name for managed)
316     * @param fields     the fields to retrieve. Leave this out for the standard fields.
317     * @return an iterable containing users matching the given email
318     */
319    public static Iterable<BoxUser.Info> getAllEnterpriseOrExternalUsers(
320        final BoxAPIConnection api,
321        final String filterTerm,
322        final String... fields
323    ) {
324        return getUsersInfoForType(api, filterTerm, "all", null, false, null, fields);
325    }
326
327    /**
328     * Gets any managed users that match the filter term as well as any external users that
329     * match the filter term. For managed users it matches any users names or emails that
330     * start with the term. For external, it only does full match on email. This method
331     * is ideal to use in the case where you have a full email for a user and you don't
332     * know if they're managed or external. Uses marker based pagination.
333     *
334     * @param api        the API connection to be used when retrieving the users.
335     * @param filterTerm The filter term to lookup users by (login for external, login or name for managed)
336     * @param usemarker  Boolean that determines whether to use marker based pagination.
337     * @param marker     The marker at which the iterator will begin.
338     * @param fields     the fields to retrieve. Leave this out for the standard fields.
339     * @return an iterable containing users matching the given email
340     */
341    public static Iterable<BoxUser.Info> getAllEnterpriseOrExternalUsers(
342        final BoxAPIConnection api,
343        final String filterTerm,
344        final boolean usemarker,
345        final String marker,
346        final String... fields
347    ) {
348        return getUsersInfoForType(api, filterTerm, "all", null, usemarker, marker, fields);
349    }
350
351    /**
352     * Gets any app users that has an exact match with the externalAppUserId term.
353     *
354     * @param api               the API connection to be used when retrieving the users.
355     * @param externalAppUserId the external app user id that has been set for app user
356     * @param fields            the fields to retrieve. Leave this out for the standard fields.
357     * @return an iterable containing users matching the given email
358     */
359    public static Iterable<BoxUser.Info> getAppUsersByExternalAppUserID(
360        final BoxAPIConnection api,
361        final String externalAppUserId,
362        final String... fields
363    ) {
364        return getUsersInfoForType(api, null, null, externalAppUserId, false, null, fields);
365    }
366
367    /**
368     * Gets any app users that has an exact match with the externalAppUserId term using marker based pagination.
369     *
370     * @param api               the API connection to be used when retrieving the users.
371     * @param externalAppUserId the external app user id that has been set for app user
372     * @param usemarker         Boolean that determines whether to use marker based pagination.
373     * @param marker            The marker at which the iterator will begin.
374     * @param fields            the fields to retrieve. Leave this out for the standard fields.
375     * @return an iterable containing users matching the given email
376     */
377    public static Iterable<BoxUser.Info> getAppUsersByExternalAppUserID(
378        final BoxAPIConnection api,
379        final String externalAppUserId,
380        final boolean usemarker,
381        String marker,
382        final String... fields
383    ) {
384        return getUsersInfoForType(api, null, null, externalAppUserId, usemarker, marker, fields);
385    }
386
387    /**
388     * Helper method to abstract out the common logic from the various users methods.
389     *
390     * @param api               the API connection to be used when retrieving the users.
391     * @param filterTerm        The filter term to lookup users by (login for external, login or name for managed)
392     * @param userType          The type of users we want to search with this request.
393     *                          Valid values are 'managed' (enterprise users), 'external' or 'all'
394     * @param externalAppUserId the external app user id that has been set for an app user
395     * @param usemarker         Boolean that determines whether to use marker based pagination.
396     * @param marker            The marker at which the iterator will begin.
397     * @param fields            the fields to retrieve. Leave this out for the standard fields.
398     * @return An iterator over the selected users.
399     */
400    private static Iterable<BoxUser.Info> getUsersInfoForType(
401        final BoxAPIConnection api,
402        final String filterTerm,
403        final String userType,
404        final String externalAppUserId,
405        final boolean usemarker,
406        final String marker,
407        final String... fields
408    ) {
409
410        final QueryStringBuilder builder = new QueryStringBuilder();
411        if (filterTerm != null) {
412            builder.appendParam("filter_term", filterTerm);
413        }
414        if (userType != null) {
415            builder.appendParam("user_type", userType);
416        }
417        if (externalAppUserId != null) {
418            builder.appendParam("external_app_user_id", externalAppUserId);
419        }
420        if (usemarker) {
421            builder.appendParam("usemarker", "true");
422        }
423        if (fields.length > 0) {
424            builder.appendParam("fields", fields);
425        }
426        final URL url = USERS_URL_TEMPLATE.buildWithQuery(api.getBaseURL(), builder.toString());
427
428        if (usemarker) {
429            return new BoxResourceIterable<BoxUser.Info>(api, url, 100, null, marker) {
430                @Override
431                protected BoxUser.Info factory(JsonObject jsonObject) {
432                    BoxUser user = new BoxUser(api, jsonObject.get("id").asString());
433                    return user.new Info(jsonObject);
434                }
435            };
436        } else {
437            return () -> new BoxUserIterator(api, url);
438        }
439    }
440
441    private static JsonArray toTrackingCodesJson(Map<String, String> trackingCodes) {
442        JsonArray trackingCodesJsonArray = new JsonArray();
443        for (String attrKey : trackingCodes.keySet()) {
444            JsonObject trackingCode = new JsonObject();
445            trackingCode.set("type", "tracking_code");
446            trackingCode.set("name", attrKey);
447            trackingCode.set("value", trackingCodes.get(attrKey));
448            trackingCodesJsonArray.add(trackingCode);
449        }
450        return trackingCodesJsonArray;
451    }
452
453    /**
454     * Gets information about this user.
455     *
456     * @param fields the optional fields to retrieve.
457     * @return info about this user.
458     */
459    public BoxUser.Info getInfo(String... fields) {
460        URL url;
461        if (fields.length > 0) {
462            String queryString = new QueryStringBuilder().appendParam("fields", fields).toString();
463            url = USER_URL_TEMPLATE.buildWithQuery(this.getAPI().getBaseURL(), queryString, this.getID());
464        } else {
465            url = USER_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID());
466        }
467        BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "GET");
468        try (BoxJSONResponse response = request.send()) {
469            JsonObject jsonObject = Json.parse(response.getJSON()).asObject();
470            return new Info(jsonObject);
471        }
472    }
473
474    /**
475     * Gets information about all of the group memberships for this user.
476     * Does not support paging.
477     *
478     * <p>Note: This method is only available to enterprise admins.</p>
479     *
480     * @return a collection of information about the group memberships for this user.
481     */
482    public Collection<BoxGroupMembership.Info> getMemberships() {
483        BoxAPIConnection api = this.getAPI();
484        URL url = USER_MEMBERSHIPS_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID());
485
486        BoxJSONRequest request = new BoxJSONRequest(api, url, "GET");
487        try (BoxJSONResponse response = request.send()) {
488            JsonObject responseJSON = Json.parse(response.getJSON()).asObject();
489
490            int entriesCount = responseJSON.get("total_count").asInt();
491            Collection<BoxGroupMembership.Info> memberships = new ArrayList<>(entriesCount);
492            JsonArray entries = responseJSON.get("entries").asArray();
493            for (JsonValue entry : entries) {
494                JsonObject entryObject = entry.asObject();
495                BoxGroupMembership membership = new BoxGroupMembership(api, entryObject.get("id").asString());
496                BoxGroupMembership.Info info = membership.new Info(entryObject);
497                memberships.add(info);
498            }
499
500            return memberships;
501        }
502    }
503
504    /**
505     * Gets information about all of the group memberships for this user as iterable with paging support.
506     *
507     * @param fields the fields to retrieve.
508     * @return an iterable with information about the group memberships for this user.
509     */
510    public Iterable<BoxGroupMembership.Info> getAllMemberships(String... fields) {
511        final QueryStringBuilder builder = new QueryStringBuilder();
512        if (fields.length > 0) {
513            builder.appendParam("fields", fields);
514        }
515        return () -> {
516            URL url = USER_MEMBERSHIPS_URL_TEMPLATE.buildWithQuery(
517                BoxUser.this.getAPI().getBaseURL(), builder.toString(), BoxUser.this.getID());
518            return new BoxGroupMembershipIterator(BoxUser.this.getAPI(), url);
519        };
520    }
521
522    /**
523     * Adds a new email alias to this user's account.
524     *
525     * @param email the email address to add as an alias.
526     * @return the newly created email alias.
527     */
528    public EmailAlias addEmailAlias(String email) {
529        return this.addEmailAlias(email, false);
530    }
531
532    /**
533     * Adds a new email alias to this user's account and confirms it without user interaction.
534     * This functionality is only available for enterprise admins.
535     *
536     * @param email       the email address to add as an alias.
537     * @param isConfirmed whether or not the email alias should be automatically confirmed.
538     * @return the newly created email alias.
539     */
540    public EmailAlias addEmailAlias(String email, boolean isConfirmed) {
541        URL url = EMAIL_ALIASES_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID());
542        BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "POST");
543
544        JsonObject requestJSON = new JsonObject()
545            .add("email", email);
546
547        if (isConfirmed) {
548            requestJSON.add("is_confirmed", isConfirmed);
549        }
550
551        request.setBody(requestJSON.toString());
552        try (BoxJSONResponse response = request.send()) {
553            JsonObject responseJSON = Json.parse(response.getJSON()).asObject();
554            return new EmailAlias(responseJSON);
555        }
556    }
557
558    /**
559     * Deletes an email alias from this user's account.
560     *
561     * <p>The IDs of the user's email aliases can be found by calling {@link #getEmailAliases}.</p>
562     *
563     * @param emailAliasID the ID of the email alias to delete.
564     */
565    public void deleteEmailAlias(String emailAliasID) {
566        URL url = EMAIL_ALIAS_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID(), emailAliasID);
567        BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "DELETE");
568        request.send().close();
569    }
570
571    /**
572     * Gets a collection of all the email aliases for this user.
573     *
574     * <p>Note that the user's primary login email is not included in the collection of email aliases.</p>
575     *
576     * @return a collection of all the email aliases for this user.
577     */
578    public Collection<EmailAlias> getEmailAliases() {
579        URL url = EMAIL_ALIASES_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID());
580        BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "GET");
581        try (BoxJSONResponse response = request.send()) {
582            JsonObject responseJSON = Json.parse(response.getJSON()).asObject();
583
584            int totalCount = responseJSON.get("total_count").asInt();
585            Collection<EmailAlias> emailAliases = new ArrayList<>(totalCount);
586            JsonArray entries = responseJSON.get("entries").asArray();
587            for (JsonValue value : entries) {
588                JsonObject emailAliasJSON = value.asObject();
589                emailAliases.add(new EmailAlias(emailAliasJSON));
590            }
591
592            return emailAliases;
593        }
594    }
595
596    /**
597     * Deletes a user from an enterprise account.
598     *
599     * @param notifyUser whether or not to send an email notification to the user that their account has been deleted.
600     * @param force      whether or not this user should be deleted even if they still own files.
601     */
602    public void delete(boolean notifyUser, boolean force) {
603        String queryString = new QueryStringBuilder()
604            .appendParam("notify", String.valueOf(notifyUser))
605            .appendParam("force", String.valueOf(force))
606            .toString();
607
608        performUserDelete(queryString);
609    }
610
611    /**
612     * Deletes a user from an enterprise account. Uses API default values to determine if request should
613     * be forced and if user should be notified.
614     */
615    public void delete() {
616        performUserDelete("");
617    }
618
619    private void performUserDelete(String queryString) {
620        URL url = USER_URL_TEMPLATE.buildWithQuery(this.getAPI().getBaseURL(), queryString, this.getID());
621        BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "DELETE");
622        request.send().close();
623    }
624
625    /**
626     * Updates the information about this user with any info fields that have been modified locally.
627     *
628     * <p>Note: This method is only available to enterprise admins.</p>
629     *
630     * @param info info the updated info.
631     * @param fields the fields to retrieve. Leave this out for the standard fields.
632     */
633    public void updateInfo(BoxUser.Info info, String... fields) {
634        QueryStringBuilder builder = new QueryStringBuilder();
635        if (fields.length > 0) {
636            builder.appendParam("fields", fields);
637        }
638        URL url = USER_URL_TEMPLATE.buildWithQuery(this.getAPI().getBaseURL(), builder.toString(), this.getID());
639        BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "PUT");
640        request.setBody(info.getPendingChanges());
641        try (BoxJSONResponse response = request.send()) {
642            JsonObject jsonObject = Json.parse(response.getJSON()).asObject();
643            info.update(jsonObject);
644        }
645    }
646
647    /**
648     * Moves all the owned content from within one user’s folder into a new folder in another user's account.
649     * You can move folders across users as long as the you have administrative permissions and the 'source'
650     * user owns the folders. Per the documentation at the link below, this will move everything from the root
651     * folder, as this is currently the only mode of operation supported.
652     * <p>
653     * See also <a href="https://developer.box.com/en/reference/put-users-id-folders-id/">https://developer.box.com/en/reference/put-users-id-folders-id/</a>
654     *
655     * @param destinationUserID the user id of the user that you wish to transfer content to.
656     * @return info for the newly created folder.
657     */
658    public BoxFolder.Info transferContent(String destinationUserID) {
659        URL url = MOVE_FOLDER_TO_USER_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID(), "0");
660        BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "PUT");
661        JsonObject destinationUser = new JsonObject();
662        destinationUser.add("id", destinationUserID);
663        JsonObject ownedBy = new JsonObject();
664        ownedBy.add("owned_by", destinationUser);
665        request.setBody(ownedBy.toString());
666        try (BoxJSONResponse response = request.send()) {
667            JsonObject responseJSON = Json.parse(response.getJSON()).asObject();
668            BoxFolder movedFolder = new BoxFolder(this.getAPI(), responseJSON.get("id").asString());
669
670            return movedFolder.new Info(responseJSON);
671        }
672    }
673
674    /**
675     * Writes avatar into specified {@link OutputStream}
676     *
677     * @param outputStream Stream where to write avatar.
678     */
679    public void downloadAvatar(OutputStream outputStream) {
680        URL url = USER_AVATAR_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID());
681        BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET");
682        writeStream(request.send(), outputStream);
683    }
684
685    /**
686     * Retrieves the avatar of a user as an InputStream.
687     *
688     * @return InputStream representing the user avater.
689     * @deprecated This method loads whole avatar image to memory.
690     * Use {@link BoxUser#downloadAvatar(OutputStream)} to store avatar in file or memory.
691     * That method uses very small buffer to store bytes in memory.
692     */
693    public InputStream getAvatar() {
694        URL url = USER_AVATAR_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID());
695        BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET");
696        BoxAPIResponse response = request.send();
697        try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
698            writeStream(response, outputStream);
699            return new ByteArrayInputStream(outputStream.toByteArray());
700        } catch (IOException e) {
701            throw new RuntimeException("Error while closing stream", e);
702        }
703    }
704
705    /**
706     * Upload avatar image to user account. Supported formats are JPG and PNG.
707     * The image size cannot exceed 1024 * 1024 pixels or 1MB.
708     *
709     * @param file File containg avatar image.
710     * @return Avatar creation response.
711     */
712    public AvatarUploadResponse uploadAvatar(File file) {
713        try {
714            return uploadAvatar(new FileInputStream(file), file.getName());
715        } catch (FileNotFoundException e) {
716            throw new RuntimeException(e);
717        }
718    }
719
720    /**
721     * Upload avatar image to user account. Supported formats are JPG and PNG.
722     * The image size cannot exceed 1024 * 1024 pixels or 1MB.
723     *
724     * @param file             {@link File} containg avatar image.
725     * @param progressListener {@link ProgressListener} set if you want to track upload progress
726     * @return Avatar creation response.
727     */
728    public AvatarUploadResponse uploadAvatar(File file, ProgressListener progressListener) {
729        try {
730            return uploadAvatar(new FileInputStream(file), file.getName(), progressListener);
731        } catch (FileNotFoundException e) {
732            throw new RuntimeException(e);
733        }
734    }
735
736    /**
737     * Upload avatar image to user account. Supported formats are JPG and PNG.
738     * The image size cannot exceed 1024 * 1024 pixels or 1MB.
739     *
740     * @param content  {@link InputStream} containing image data
741     * @param fileName file name with extention what will be used to determine content type
742     * @return Avatar creation response.
743     */
744    public AvatarUploadResponse uploadAvatar(InputStream content, String fileName) {
745        return uploadAvatar(content, fileName, null);
746    }
747
748    /**
749     * Upload avatar image to user account. Supported formats are JPG and PNG.
750     * The image size cannot exceed 1024 * 1024 pixels or 1MB.
751     *
752     * @param content          {@link InputStream} containing image data
753     * @param fileName         file name with extention what will be used to determine content type
754     * @param progressListener {@link ProgressListener} set if you want to track upload progress
755     * @return Avatar creation response.
756     */
757    public AvatarUploadResponse uploadAvatar(InputStream content, String fileName, ProgressListener progressListener) {
758        URL url = USER_AVATAR_TEMPLATE.build(getAPI().getBaseURL(), this.getID());
759        BoxImageMultipartRequest request = new BoxImageMultipartRequest(getAPI(), url, "pic");
760        request.setFile(content, fileName);
761
762        BoxAPIResponse response = null;
763        try {
764            if (progressListener != null) {
765                response = request.send(progressListener);
766            } else {
767                response = request.send();
768            }
769
770            return parseUploadAvatarResponse(response);
771        } finally {
772            Optional.ofNullable(response).ifPresent(BoxAPIResponse::close);
773        }
774    }
775
776    /**
777     * Removes avatar from user account.
778     */
779    public void deleteAvatar() {
780        URL url = USER_AVATAR_TEMPLATE.build(getAPI().getBaseURL(), this.getID());
781        BoxAPIRequest request = new BoxAPIRequest(getAPI(), url, DELETE);
782        request.send().close();
783    }
784
785    private AvatarUploadResponse parseUploadAvatarResponse(BoxAPIResponse response) {
786        JsonObject responseObject = Json.parse(response.bodyToString()).asObject();
787        JsonObject picUrls = responseObject.get("pic_urls").asObject();
788        return new AvatarUploadResponse(
789            picUrls.getString("small", null),
790            picUrls.getString("large", null),
791            picUrls.getString("preview", null)
792        );
793    }
794
795    /**
796     * Enumerates the possible roles that a user can have within an enterprise.
797     */
798    public enum Role {
799        /**
800         * The user is an administrator of their enterprise.
801         */
802        ADMIN("admin"),
803
804        /**
805         * The user is a co-administrator of their enterprise.
806         */
807        COADMIN("coadmin"),
808
809        /**
810         * The user is a regular user within their enterprise.
811         */
812        USER("user");
813
814        private final String jsonValue;
815
816        Role(String jsonValue) {
817            this.jsonValue = jsonValue;
818        }
819
820        static Role fromJSONValue(String jsonValue) {
821            return Role.valueOf(jsonValue.toUpperCase());
822        }
823
824        String toJSONValue() {
825            return this.jsonValue;
826        }
827    }
828
829    /**
830     * Enumerates the possible statuses that a user's account can have.
831     */
832    public enum Status {
833        /**
834         * The user's account is active.
835         */
836        ACTIVE("active"),
837
838        /**
839         * The user's account is inactive.
840         */
841        INACTIVE("inactive"),
842
843        /**
844         * The user's account cannot delete or edit content.
845         */
846        CANNOT_DELETE_EDIT("cannot_delete_edit"),
847
848        /**
849         * The user's account cannot delete, edit, or upload content.
850         */
851        CANNOT_DELETE_EDIT_UPLOAD("cannot_delete_edit_upload");
852
853        private final String jsonValue;
854
855        Status(String jsonValue) {
856            this.jsonValue = jsonValue;
857        }
858
859        static Status fromJSONValue(String jsonValue) {
860            return Status.valueOf(jsonValue.toUpperCase());
861        }
862
863        String toJSONValue() {
864            return this.jsonValue;
865        }
866    }
867
868    /**
869     * Contains information about a BoxUser.
870     */
871    public class Info extends BoxCollaborator.Info {
872        private String login;
873        private Role role;
874        private String language;
875        private String timezone;
876        private long spaceAmount;
877        private long spaceUsed;
878        private long maxUploadSize;
879        private boolean canSeeManagedUsers;
880        private boolean isSyncEnabled;
881        private boolean isExternalCollabRestricted;
882        private Status status;
883        private String jobTitle;
884        private String phone;
885        private String address;
886        private String avatarURL;
887        private BoxNotificationEmail notificationEmail;
888        private boolean isExemptFromDeviceLimits;
889        private boolean isExemptFromLoginVerification;
890        private boolean isPasswordResetRequired;
891        private boolean isPlatformAccessOnly;
892        private String externalAppUserId;
893        private BoxEnterprise enterprise;
894        private List<String> myTags;
895        private String hostname;
896        private Map<String, String> trackingCodes;
897
898        /**
899         * Constructs an empty Info object.
900         */
901        public Info() {
902            super();
903        }
904
905        /**
906         * Constructs an Info object by parsing information from a JSON string.
907         *
908         * @param json the JSON string to parse.
909         */
910        public Info(String json) {
911            super(json);
912        }
913
914        Info(JsonObject jsonObject) {
915            super(jsonObject);
916        }
917
918        @Override
919        public BoxUser getResource() {
920            return BoxUser.this;
921        }
922
923        /**
924         * Gets the email address the user uses to login.
925         *
926         * @return the email address the user uses to login.
927         */
928        public String getLogin() {
929            return this.login;
930        }
931
932        /**
933         * Sets the email address the user uses to login. The new login must be one of the user's already confirmed
934         * email aliases.
935         *
936         * @param login one of the user's confirmed email aliases.
937         */
938        public void setLogin(String login) {
939            this.login = login;
940            this.addPendingChange("login", login);
941        }
942
943        /**
944         * Gets the user's enterprise role.
945         *
946         * @return the user's enterprise role.
947         */
948        public Role getRole() {
949            return this.role;
950        }
951
952        /**
953         * Sets the user's role in their enterprise.
954         *
955         * @param role the user's new role in their enterprise.
956         */
957        public void setRole(Role role) {
958            this.role = role;
959            this.addPendingChange("role", role.name().toLowerCase());
960        }
961
962        /**
963         * Gets the language of the user.
964         *
965         * @return the language of the user.
966         */
967        public String getLanguage() {
968            return this.language;
969        }
970
971        /**
972         * Sets the language of the user.
973         *
974         * @param language the new language of the user.
975         */
976        public void setLanguage(String language) {
977            this.language = language;
978            this.addPendingChange("language", language);
979        }
980
981        /**
982         * Gets the timezone of the user.
983         *
984         * @return the timezone of the user.
985         */
986        public String getTimezone() {
987            return this.timezone;
988        }
989
990        /**
991         * Sets the timezone of the user.
992         *
993         * @param timezone the new timezone of the user.
994         */
995        public void setTimezone(String timezone) {
996            this.timezone = timezone;
997            this.addPendingChange("timezone", timezone);
998        }
999
1000        /**
1001         * Gets the user's total available space in bytes.
1002         *
1003         * @return the user's total available space in bytes.
1004         */
1005        public long getSpaceAmount() {
1006            return this.spaceAmount;
1007        }
1008
1009        /**
1010         * Sets the user's total available space in bytes.
1011         *
1012         * @param spaceAmount the new amount of space available to the user in bytes, or -1 for unlimited storage.
1013         */
1014        public void setSpaceAmount(long spaceAmount) {
1015            this.spaceAmount = spaceAmount;
1016            this.addPendingChange("space_amount", spaceAmount);
1017        }
1018
1019        /**
1020         * Gets the amount of space the user has used in bytes.
1021         *
1022         * @return the amount of space the user has used in bytes.
1023         */
1024        public long getSpaceUsed() {
1025            return this.spaceUsed;
1026        }
1027
1028        /**
1029         * Gets the maximum individual file size in bytes the user can have.
1030         *
1031         * @return the maximum individual file size in bytes the user can have.
1032         */
1033        public long getMaxUploadSize() {
1034            return this.maxUploadSize;
1035        }
1036
1037        /**
1038         * Gets the user's current account status.
1039         *
1040         * @return the user's current account status.
1041         */
1042        public Status getStatus() {
1043            return this.status;
1044        }
1045
1046        /**
1047         * Sets the user's current account status.
1048         *
1049         * @param status the user's new account status.
1050         */
1051        public void setStatus(Status status) {
1052            this.status = status;
1053            this.addPendingChange("status", status.name().toLowerCase());
1054        }
1055
1056        /**
1057         * Gets the job title of the user.
1058         *
1059         * @return the job title of the user.
1060         */
1061        public String getJobTitle() {
1062            return this.jobTitle;
1063        }
1064
1065        /**
1066         * Sets the job title of the user.
1067         *
1068         * @param jobTitle the new job title of the user.
1069         */
1070        public void setJobTitle(String jobTitle) {
1071            this.jobTitle = jobTitle;
1072            this.addPendingChange("job_title", jobTitle);
1073        }
1074
1075        /**
1076         * Gets the phone number of the user.
1077         *
1078         * @return the phone number of the user.
1079         */
1080        public String getPhone() {
1081            return this.phone;
1082        }
1083
1084        /**
1085         * Sets the phone number of the user.
1086         *
1087         * @param phone the new phone number of the user.
1088         */
1089        public void setPhone(String phone) {
1090            this.phone = phone;
1091            this.addPendingChange("phone", phone);
1092        }
1093
1094        /**
1095         * Gets the address of the user.
1096         *
1097         * @return the address of the user.
1098         */
1099        public String getAddress() {
1100            return this.address;
1101        }
1102
1103        /**
1104         * Sets the address of the user.
1105         *
1106         * @param address the new address of the user.
1107         */
1108        public void setAddress(String address) {
1109            this.address = address;
1110            this.addPendingChange("address", address);
1111        }
1112
1113        /**
1114         * Gets the URL of the user's avatar.
1115         *
1116         * @return the URL of the user's avatar.
1117         */
1118        public String getAvatarURL() {
1119            return this.avatarURL;
1120        }
1121
1122        /**
1123         * Gets the user's alternate notification email address to which email notifications are sent.
1124         *
1125         * @return the user's notification email address.
1126         */
1127        public BoxNotificationEmail getNotificationEmail() {
1128            return this.notificationEmail;
1129        }
1130
1131        /**
1132         * Sets the user's notification email address.
1133         *
1134         * @param notificationEmail the user's new notification email address.
1135         */
1136        public void setNotificationEmail(BoxNotificationEmail notificationEmail) {
1137            this.notificationEmail = notificationEmail;
1138        }
1139
1140        /**
1141         * Gets the enterprise that the user belongs to.
1142         *
1143         * @return the enterprise that the user belongs to.
1144         */
1145        public BoxEnterprise getEnterprise() {
1146            return this.enterprise;
1147        }
1148
1149        /**
1150         * Removes the user from their enterprise and converts them to a standalone free user.
1151         */
1152        public void removeEnterprise() {
1153            this.removeChildObject("enterprise");
1154            this.enterprise = null;
1155            this.addChildObject("enterprise", null);
1156        }
1157
1158        /**
1159         * Gets whether or not the user can use Box Sync.
1160         *
1161         * @return true if the user can use Box Sync; otherwise false.
1162         */
1163        public boolean getIsSyncEnabled() {
1164            return this.isSyncEnabled;
1165        }
1166
1167        /**
1168         * Sets whether or not the user can use Box Sync.
1169         *
1170         * @param enabled whether or not the user can use Box Sync.
1171         */
1172        public void setIsSyncEnabled(boolean enabled) {
1173            this.isSyncEnabled = enabled;
1174            this.addPendingChange("is_sync_enabled", enabled);
1175        }
1176
1177        /**
1178         * Gets whether this user is allowed or not to collaborate with users outside their enterprise.
1179         *
1180         * @return true if this user is not allowed to collaborate with users outside their enterprise; otherwise false.
1181         */
1182        public boolean getIsExternalCollabRestricted() {
1183            return this.isExternalCollabRestricted;
1184        }
1185
1186        /**
1187         * Sets whether this user is allowed or not to collaborate with users outside their enterprise.
1188         *
1189         * @param isExternalCollabRestricted whether the user is allowed to collaborate outside their enterprise.
1190         */
1191        public void setIsExternalCollabRestricted(boolean isExternalCollabRestricted) {
1192            this.isExternalCollabRestricted = isExternalCollabRestricted;
1193            this.addPendingChange("is_external_collab_restricted", isExternalCollabRestricted);
1194        }
1195
1196        /**
1197         * Gets whether or not the user can see other enterprise users in their contact list.
1198         *
1199         * @return true if the user can see other enterprise users in their contact list; otherwise false.
1200         */
1201        public boolean getCanSeeManagedUsers() {
1202            return this.canSeeManagedUsers;
1203        }
1204
1205        /**
1206         * Sets whether or not the user can see other enterprise users in their contact list.
1207         *
1208         * @param canSeeManagedUsers whether or not the user can see other enterprise users in their contact list.
1209         */
1210        public void setCanSeeManagedUsers(boolean canSeeManagedUsers) {
1211            this.canSeeManagedUsers = canSeeManagedUsers;
1212            this.addPendingChange("can_see_managed_users", canSeeManagedUsers);
1213        }
1214
1215        /**
1216         * Gets whether or not the user is exempt from enterprise device limits.
1217         *
1218         * @return true if the user is exempt from enterprise device limits; otherwise false.
1219         */
1220        public boolean getIsExemptFromDeviceLimits() {
1221            return this.isExemptFromDeviceLimits;
1222        }
1223
1224        /**
1225         * Sets whether or not the user is exempt from enterprise device limits.
1226         *
1227         * @param isExemptFromDeviceLimits whether or not the user is exempt from enterprise device limits.
1228         */
1229        public void setIsExemptFromDeviceLimits(boolean isExemptFromDeviceLimits) {
1230            this.isExemptFromDeviceLimits = isExemptFromDeviceLimits;
1231            this.addPendingChange("is_exempt_from_device_limits", isExemptFromDeviceLimits);
1232        }
1233
1234        /**
1235         * Gets whether or not the user must use two-factor authentication.
1236         *
1237         * @return true if the user must use two-factor authentication; otherwise false.
1238         */
1239        public boolean getIsExemptFromLoginVerification() {
1240            return this.isExemptFromLoginVerification;
1241        }
1242
1243        /**
1244         * Sets whether or not the user must use two-factor authentication.
1245         *
1246         * @param isExemptFromLoginVerification whether or not the user must use two-factor authentication.
1247         */
1248        public void setIsExemptFromLoginVerification(boolean isExemptFromLoginVerification) {
1249            this.isExemptFromLoginVerification = isExemptFromLoginVerification;
1250            this.addPendingChange("is_exempt_from_login_verification", isExemptFromLoginVerification);
1251        }
1252
1253        /**
1254         * Gets whether or not the user is required to reset password.
1255         *
1256         * @return true if the user is required to reset password; otherwise false.
1257         */
1258        public boolean getIsPasswordResetRequired() {
1259            return this.isPasswordResetRequired;
1260        }
1261
1262        /**
1263         * Sets whether or not the user is required to reset password.
1264         *
1265         * @param isPasswordResetRequired whether or not the user is required to reset password.
1266         */
1267        public void setIsPasswordResetRequired(boolean isPasswordResetRequired) {
1268            this.isPasswordResetRequired = isPasswordResetRequired;
1269            this.addPendingChange("is_password_reset_required", isPasswordResetRequired);
1270        }
1271
1272        /**
1273         * Gets whether or not the user we are creating is an app user with Box Developer Edition.
1274         *
1275         * @return true if the new user is an app user for Box Developer Addition; otherwise false.
1276         */
1277        public boolean getIsPlatformAccessOnly() {
1278            return this.isPlatformAccessOnly;
1279        }
1280
1281        /**
1282         * Gets the external app user id that has been set for the app user.
1283         *
1284         * @return the external app user id.
1285         */
1286        public String getExternalAppUserId() {
1287            return this.externalAppUserId;
1288        }
1289
1290        /**
1291         * Sets the external app user id.
1292         *
1293         * @param externalAppUserId external app user id.
1294         */
1295        public void setExternalAppUserId(String externalAppUserId) {
1296            this.externalAppUserId = externalAppUserId;
1297            this.addPendingChange("external_app_user_id", externalAppUserId);
1298        }
1299
1300        /**
1301         * Gets the tags for all files and folders owned by this user.
1302         *
1303         * @return the tags for all files and folders owned by this user.
1304         */
1305        public List<String> getMyTags() {
1306            return this.myTags;
1307        }
1308
1309        /**
1310         * Gets the root (protocol, subdomain, domain) of any links that need to be generated for this user.
1311         *
1312         * @return the root (protocol, subdomain, domain) of any links that need to be generated for this user.
1313         */
1314        public String getHostname() {
1315            return this.hostname;
1316        }
1317
1318        /**
1319         * Gets the tracking defined for each entity.
1320         *
1321         * @return a Map with tracking codes.
1322         */
1323        public Map<String, String> getTrackingCodes() {
1324            return this.trackingCodes;
1325        }
1326
1327        /**
1328         * Allows admin to set attributes specific for a group of users.
1329         *
1330         * @param trackingCodes a Map representing the user's new tracking codes
1331         */
1332        public void setTrackingCodes(Map<String, String> trackingCodes) {
1333            this.trackingCodes = trackingCodes;
1334            this.addPendingChange("tracking_codes", toTrackingCodesJson(this.trackingCodes));
1335        }
1336
1337        /**
1338         * Allows the admin to append new tracking codes to the previous existing list.
1339         *
1340         * @param name  the name or `key` of the attribute to set.
1341         * @param value the value of the attribute to set.
1342         */
1343        public void appendTrackingCodes(String name, String value) {
1344            this.getTrackingCodes().put(name, value);
1345            this.addPendingChange("tracking_codes", toTrackingCodesJson(this.trackingCodes));
1346        }
1347
1348        @SuppressWarnings("checkstyle:MissingSwitchDefault")
1349        @Override
1350        protected void parseJSONMember(JsonObject.Member member) {
1351            super.parseJSONMember(member);
1352
1353            JsonValue value = member.getValue();
1354            String memberName = member.getName();
1355            try {
1356                switch (memberName) {
1357                    case "login":
1358                        this.login = value.asString();
1359                        break;
1360                    case "role":
1361                        this.role = Role.fromJSONValue(value.asString());
1362                        break;
1363                    case "language":
1364                        this.language = value.asString();
1365                        break;
1366                    case "timezone":
1367                        this.timezone = value.asString();
1368                        break;
1369                    case "space_amount":
1370                        this.spaceAmount = Double.valueOf(value.toString()).longValue();
1371                        break;
1372                    case "space_used":
1373                        this.spaceUsed = Double.valueOf(value.toString()).longValue();
1374                        break;
1375                    case "max_upload_size":
1376                        this.maxUploadSize = Double.valueOf(value.toString()).longValue();
1377                        break;
1378                    case "status":
1379                        this.status = Status.fromJSONValue(value.asString());
1380                        break;
1381                    case "job_title":
1382                        this.jobTitle = value.asString();
1383                        break;
1384                    case "phone":
1385                        this.phone = value.asString();
1386                        break;
1387                    case "address":
1388                        this.address = value.asString();
1389                        break;
1390                    case "avatar_url":
1391                        this.avatarURL = value.asString();
1392                        break;
1393                    case "notification_email":
1394                        if (value.isObject()) {
1395                            this.notificationEmail = new BoxNotificationEmail(value.asObject());
1396                        } else {
1397                            this.notificationEmail = null;
1398                        }
1399                        break;
1400                    case "can_see_managed_users":
1401                        this.canSeeManagedUsers = value.asBoolean();
1402                        break;
1403                    case "is_sync_enabled":
1404                        this.isSyncEnabled = value.asBoolean();
1405                        break;
1406                    case "is_external_collab_restricted":
1407                        this.isExternalCollabRestricted = value.asBoolean();
1408                        break;
1409                    case "is_exempt_from_device_limits":
1410                        this.isExemptFromDeviceLimits = value.asBoolean();
1411                        break;
1412                    case "is_exempt_from_login_verification":
1413                        this.isExemptFromLoginVerification = value.asBoolean();
1414                        break;
1415                    case "is_password_reset_required":
1416                        this.isPasswordResetRequired = value.asBoolean();
1417                        break;
1418                    case "is_platform_access_only":
1419                        this.isPlatformAccessOnly = value.asBoolean();
1420                        break;
1421                    case "external_app_user_id":
1422                        this.externalAppUserId = value.asString();
1423                        break;
1424                    case "enterprise":
1425                        JsonObject jsonObject = value.asObject();
1426                        if (this.enterprise == null) {
1427                            this.enterprise = new BoxEnterprise(jsonObject);
1428                        } else {
1429                            this.enterprise.update(jsonObject);
1430                        }
1431                        break;
1432                    case "my_tags":
1433                        this.myTags = this.parseMyTags(value.asArray());
1434                        break;
1435                    case "hostname":
1436                        this.hostname = value.asString();
1437                        break;
1438                    case "tracking_codes":
1439                        this.trackingCodes = this.parseTrackingCodes(value.asArray());
1440                        break;
1441                }
1442            } catch (Exception e) {
1443                throw new BoxDeserializationException(memberName, value.toString(), e);
1444            }
1445
1446        }
1447
1448        private List<String> parseMyTags(JsonArray jsonArray) {
1449            List<String> myTags = new ArrayList<>(jsonArray.size());
1450            for (JsonValue value : jsonArray) {
1451                myTags.add(value.asString());
1452            }
1453
1454            return myTags;
1455        }
1456
1457        private Map<String, String> parseTrackingCodes(JsonArray jsonArray) {
1458            Map<String, String> result = new HashMap<>();
1459            if (jsonArray == null) {
1460                return null;
1461            }
1462            List<JsonValue> valuesList = jsonArray.values();
1463            for (JsonValue jsonValue : valuesList) {
1464                JsonObject object = jsonValue.asObject();
1465                result.put(object.get("name").asString(), object.get("value").asString());
1466            }
1467            return result;
1468        }
1469    }
1470}