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