@@ -300,6 +300,7 @@ public class ApiConstants { | |||
public static final String API_TAG_SEARCH = "search"; | |||
public static final String API_TAG_BACKUP_RESTORE = "backup and restore"; | |||
public static final String API_TAG_NODE = "node"; | |||
public static final String API_TAG_NODE_MANAGER = "node manager"; | |||
public static String getToken(String json) { | |||
if (json == null) return null; | |||
@@ -2,15 +2,17 @@ | |||
* Copyright (c) 2020 Bubble, Inc. All rights reserved. | |||
* For personal (non-commercial) use, see license: https://getbubblenow.com/bubble-license/ | |||
*/ | |||
package bubble.server; | |||
package bubble.model; | |||
import com.fasterxml.jackson.annotation.JsonIgnore; | |||
import io.swagger.v3.oas.annotations.media.Schema; | |||
import lombok.Getter; | |||
import lombok.Setter; | |||
import java.util.HashMap; | |||
import java.util.Map; | |||
@Schema | |||
public class AppLinks extends BasicAppLinks { | |||
@JsonIgnore @Getter @Setter private Map<String, BasicAppLinks> locale = new HashMap<>(); |
@@ -0,0 +1,19 @@ | |||
/** | |||
* Copyright (c) 2020 Bubble, Inc. All rights reserved. | |||
* For personal (non-commercial) use, see license: https://getbubblenow.com/bubble-license/ | |||
*/ | |||
package bubble.model; | |||
import lombok.Getter; | |||
import lombok.Setter; | |||
import org.cobbzilla.wizard.model.entityconfig.annotations.ECField; | |||
public class BasicAppLinks { | |||
@ECField @Getter @Setter private String ios; | |||
@ECField @Getter @Setter private String android; | |||
@ECField @Getter @Setter private String windows; | |||
@ECField @Getter @Setter private String macosx; | |||
@ECField @Getter @Setter private String linux; | |||
} |
@@ -35,6 +35,7 @@ import static bubble.ApiConstants.ENTITY_CONFIGS_ENDPOINT; | |||
import static java.lang.Boolean.TRUE; | |||
import static org.cobbzilla.util.daemon.ZillaRuntime.die; | |||
import static org.cobbzilla.util.http.HttpContentTypes.APPLICATION_JSON; | |||
import static org.cobbzilla.util.http.HttpStatusCodes.SC_OK; | |||
import static org.cobbzilla.util.io.FileUtil.abs; | |||
import static org.cobbzilla.util.string.StringUtil.packagePath; | |||
import static org.cobbzilla.wizard.resources.ResourceUtil.*; | |||
@@ -57,7 +58,7 @@ public class EntityConfigsResource extends AbstractEntityConfigsResource { | |||
description="Set a configuration parameter to `true`", | |||
parameters={@Parameter(name="param", description="the name of the parameter to set")}, | |||
responses={ | |||
@ApiResponse(description="the value that was set", | |||
@ApiResponse(responseCode=SC_OK, description="the value that was set", | |||
content={@Content(mediaType=APPLICATION_JSON, examples={ | |||
@ExampleObject(name="should always return true", value="true") | |||
} | |||
@@ -79,7 +80,7 @@ public class EntityConfigsResource extends AbstractEntityConfigsResource { | |||
@Parameter(name="value", description="the value to set the parameter to") | |||
}, | |||
responses={ | |||
@ApiResponse(description="the value that was set", | |||
@ApiResponse(responseCode=SC_OK, description="the value that was set", | |||
content={@Content(mediaType=APPLICATION_JSON, examples={ | |||
@ExampleObject(name="if the value was the String 'foo'", value="\"foo\"") | |||
} | |||
@@ -30,6 +30,7 @@ import java.util.Map; | |||
import static bubble.ApiConstants.ID_ENDPOINT; | |||
import static org.cobbzilla.util.http.HttpContentTypes.APPLICATION_JSON; | |||
import static org.cobbzilla.util.http.HttpStatusCodes.SC_OK; | |||
import static org.cobbzilla.wizard.resources.ResourceUtil.*; | |||
import static org.cobbzilla.wizard.server.config.OpenApiConfiguration.API_TAG_UTILITY; | |||
import static org.cobbzilla.wizard.server.config.OpenApiConfiguration.SEC_API_KEY; | |||
@@ -53,7 +54,7 @@ public class IdentityResource { | |||
description="Searches all model objects by ID. The id parameter is typically a UUID or name", | |||
parameters={@Parameter(name="id", description="an identifier (typically UUID or name) to search for")}, | |||
responses={ | |||
@ApiResponse(description="a JSON object where the property names are entity types, and a property's corresponding value is the object of that type found with the given ID", | |||
@ApiResponse(responseCode=SC_OK, description="a JSON object where the property names are entity types, and a property's corresponding value is the object of that type found with the given ID", | |||
content={@Content(mediaType=APPLICATION_JSON, examples={ | |||
@ExampleObject(name="usually a UUID only matches one object", value="{\"CloudService\": {\"uuid\": \"the-ID-you-searched-for\", \"other-cloud-service-fields\": \"would-be-shown\"}}"), | |||
@ExampleObject(name="a UUID for an Account also matches the AccountPolicy", value="{\"Account\": {\"uuid\": \"the-ID-you-searched-for\", \"other-account-fields\": \"would-be-shown\"}, \"AccountPolicy\": {\"uuid\": \"the-ID-you-searched-for\", \"other-policy-fields\": \"would-be-shown\"}}"), | |||
@@ -23,6 +23,7 @@ import javax.ws.rs.core.Response; | |||
import static bubble.ApiConstants.*; | |||
import static org.cobbzilla.util.http.HttpContentTypes.APPLICATION_JSON; | |||
import static org.cobbzilla.util.http.HttpStatusCodes.SC_OK; | |||
import static org.cobbzilla.wizard.resources.ResourceUtil.ok; | |||
import static org.cobbzilla.wizard.resources.ResourceUtil.userPrincipal; | |||
import static org.cobbzilla.wizard.server.config.OpenApiConfiguration.SEC_API_KEY; | |||
@@ -50,7 +51,7 @@ public class SearchResource { | |||
@Parameter(name=Q_SORT, description="sort field. prefix with + or - to indicate ascending/descending") | |||
}, | |||
responses={ | |||
@ApiResponse(description="a SearchResults object, or if meta was true then a SqlViewField[] array") | |||
@ApiResponse(responseCode=SC_OK, description="a SearchResults object, or if meta was true then a SqlViewField[] array") | |||
} | |||
) | |||
public Response search(@Context Request req, | |||
@@ -80,7 +81,7 @@ public class SearchResource { | |||
@Parameter(name=Q_SORT, description="sort field. prefix with + or - to indicate ascending/descending") | |||
}, | |||
responses={ | |||
@ApiResponse(description="a SearchResults object, or if meta was true then a SqlViewField[] array") | |||
@ApiResponse(responseCode=SC_OK, description="a SearchResults object, or if meta was true then a SqlViewField[] array") | |||
} | |||
) | |||
public Response search(@Context Request req, | |||
@@ -22,6 +22,7 @@ import javax.ws.rs.core.Context; | |||
import javax.ws.rs.core.Response; | |||
import static org.cobbzilla.util.http.HttpContentTypes.APPLICATION_JSON; | |||
import static org.cobbzilla.util.http.HttpStatusCodes.SC_OK; | |||
import static org.cobbzilla.wizard.resources.ResourceUtil.*; | |||
import static org.cobbzilla.wizard.server.config.OpenApiConfiguration.SEC_API_KEY; | |||
@@ -46,7 +47,7 @@ public class TagsResource { | |||
summary="Set a tag", | |||
description="Set a tag", | |||
parameters={@Parameter(name="name", description="name of the tag")}, | |||
responses={@ApiResponse(description="a BubbleTags object representing the current list of tags")} | |||
responses={@ApiResponse(responseCode=SC_OK, description="a BubbleTags object representing the current list of tags")} | |||
) | |||
public Response set(@Context ContainerRequest ctx, | |||
@PathParam("name") String name, | |||
@@ -29,8 +29,7 @@ import java.util.List; | |||
import static bubble.ApiConstants.API_TAG_ACCOUNT_OBJECTS; | |||
import static org.cobbzilla.util.http.HttpContentTypes.APPLICATION_JSON; | |||
import static org.cobbzilla.util.http.HttpStatusCodes.SC_NOT_FOUND; | |||
import static org.cobbzilla.util.http.HttpStatusCodes.SC_PRECONDITION_FAILED; | |||
import static org.cobbzilla.util.http.HttpStatusCodes.*; | |||
import static org.cobbzilla.util.reflect.ReflectionUtil.getFirstTypeParam; | |||
import static org.cobbzilla.util.reflect.ReflectionUtil.instantiate; | |||
import static org.cobbzilla.wizard.resources.ResourceUtil.*; | |||
@@ -69,7 +68,7 @@ public class AccountOwnedResource<E extends HasAccount, DAO extends AccountOwned | |||
tags={API_TAG_ACCOUNT_OBJECTS}, | |||
summary="List objects", | |||
description="List objects", | |||
responses={@ApiResponse(description="an array of objects")} | |||
responses={@ApiResponse(responseCode=SC_OK, description="an array of objects")} | |||
) | |||
public Response listEntities(@Context Request req, | |||
@Context ContainerRequest ctx) { | |||
@@ -141,7 +140,7 @@ public class AccountOwnedResource<E extends HasAccount, DAO extends AccountOwned | |||
summary="Find by identifier", | |||
description="Find by identifier", | |||
responses={ | |||
@ApiResponse(description="the object, if found"), | |||
@ApiResponse(responseCode=SC_OK, description="the object, if found"), | |||
@ApiResponse(responseCode=SC_NOT_FOUND, description="if the object does not exist") | |||
} | |||
) | |||
@@ -174,7 +173,7 @@ public class AccountOwnedResource<E extends HasAccount, DAO extends AccountOwned | |||
summary="Create a new object", | |||
description="Create a new object. If validation errors occur, status "+SC_PRECONDITION_FAILED+" is returned and the response will contain an array of errors. Within each error, the `messageTemplate` field refers to messages that can be localized using the /messages resource", | |||
responses={ | |||
@ApiResponse(description="the object that was created"), | |||
@ApiResponse(responseCode=SC_OK, description="the object that was created"), | |||
@ApiResponse(responseCode=SC_PRECONDITION_FAILED, description="validation errors occurred", | |||
content={@Content(mediaType=APPLICATION_JSON, examples={ | |||
@ExampleObject(name="validation errors", value="[{\"messageTemplate\": \"some.symbolic.error\", \"message\": \"some default English message\"}]") | |||
@@ -216,7 +215,7 @@ public class AccountOwnedResource<E extends HasAccount, DAO extends AccountOwned | |||
description="Update an new object. For many types, the object will be created if it does not exist. If validation errors occur, status "+SC_PRECONDITION_FAILED+" is returned and the response will contain an array of errors. Within each error, the `messageTemplate` field refers to messages that can be localized using the /messages resource", | |||
parameters={@Parameter(name="id", description="the UUID (or name, if allowed) of the object to update")}, | |||
responses={ | |||
@ApiResponse(description="the object that was updated"), | |||
@ApiResponse(responseCode=SC_OK, description="the object that was updated"), | |||
@ApiResponse(responseCode=SC_NOT_FOUND, description="no object exists with the given id"), | |||
@ApiResponse(responseCode=SC_PRECONDITION_FAILED, description="validation errors occurred", | |||
content={@Content(mediaType=APPLICATION_JSON, examples={ | |||
@@ -253,7 +252,7 @@ public class AccountOwnedResource<E extends HasAccount, DAO extends AccountOwned | |||
description="Delete an existing object", | |||
parameters={@Parameter(name="id", description="the UUID (or name, if allowed) of the object to delete")}, | |||
responses={ | |||
@ApiResponse(description="the object that was deleted"), | |||
@ApiResponse(responseCode=SC_OK, description="the object that was deleted"), | |||
@ApiResponse(responseCode=SC_NOT_FOUND, description="no object exists with the given id") | |||
} | |||
) | |||
@@ -19,6 +19,7 @@ import javax.ws.rs.core.Context; | |||
import javax.ws.rs.core.Response; | |||
import static javax.ws.rs.core.MediaType.APPLICATION_JSON; | |||
import static org.cobbzilla.util.http.HttpStatusCodes.SC_OK; | |||
import static org.cobbzilla.wizard.resources.ResourceUtil.*; | |||
import static org.cobbzilla.wizard.server.config.OpenApiConfiguration.SEC_API_KEY; | |||
@@ -36,7 +37,7 @@ public class AccountPromotionsResource { | |||
@Operation(security=@SecurityRequirement(name=SEC_API_KEY), | |||
summary="List promotions for account", | |||
description="List promotions for account", | |||
responses={@ApiResponse(description="an array of Promotion objects owned by the Account")} | |||
responses={@ApiResponse(responseCode=SC_OK, description="an array of Promotion objects owned by the Account")} | |||
) | |||
public Response listPromotions(@Context Request req, | |||
@Context ContainerRequest ctx) { | |||
@@ -49,7 +50,7 @@ public class AccountPromotionsResource { | |||
@Operation(security=@SecurityRequirement(name=SEC_API_KEY), | |||
summary="Add a promotion to an account. Must be admin.", | |||
description="Add a promotion to an account. Must be admin.", | |||
responses={@ApiResponse(description="an array of Promotion objects owned by the Account")} | |||
responses={@ApiResponse(responseCode=SC_OK, description="an array of Promotion objects owned by the Account")} | |||
) | |||
public Response adminAddPromotion(@Context Request req, | |||
@Context ContainerRequest ctx, | |||
@@ -63,7 +64,7 @@ public class AccountPromotionsResource { | |||
@Operation(security=@SecurityRequirement(name=SEC_API_KEY), | |||
summary="Remove a promotion from an account. Must be admin.", | |||
description="Remove a promotion from an account. Must be admin.", | |||
responses={@ApiResponse(description="an array of Promotion objects owned by the Account")} | |||
responses={@ApiResponse(responseCode=SC_OK, description="an array of Promotion objects owned by the Account")} | |||
) | |||
public Response adminRemovePromotion(@Context Request req, | |||
@Context ContainerRequest ctx, | |||
@@ -83,7 +83,7 @@ public class AccountsResource { | |||
summary="List all accounts", | |||
description="List all accounts. Must be admin.", | |||
responses={ | |||
@ApiResponse(description="an array of Account objects"), | |||
@ApiResponse(responseCode=SC_OK, description="an array of Account objects"), | |||
@ApiResponse(responseCode=SC_FORBIDDEN, description="forbidden if not admin") | |||
} | |||
) | |||
@@ -98,7 +98,7 @@ public class AccountsResource { | |||
summary="Find account by UUID or email. Non-admins can only find themselves.", | |||
description="Find account by UUID or email. Non-admins can only find themselves.", | |||
responses={ | |||
@ApiResponse(responseCode=SC_OK, description="the Account object that was found", ref="#/components/schemas/Account"), | |||
@ApiResponse(responseCode=SC_OK, description="the Account object that was found"), | |||
@ApiResponse(responseCode=SC_FORBIDDEN, description="forbidden if not admin") | |||
} | |||
) | |||
@@ -114,7 +114,7 @@ public class AccountsResource { | |||
summary="Create a new account", | |||
description="Create a new account. Must be admin.", | |||
responses={ | |||
@ApiResponse(description="the Account object that was just created", ref="#/components/schemas/Account"), | |||
@ApiResponse(responseCode=SC_OK, description="the Account object that was just created"), | |||
@ApiResponse(responseCode=SC_FORBIDDEN, description="forbidden if not admin") | |||
} | |||
) | |||
@@ -178,7 +178,7 @@ public class AccountsResource { | |||
description="Download all data for user. Must be admin.", | |||
parameters={@Parameter(name="id", description="UUID or email of the Account")}, | |||
responses={ | |||
@ApiResponse(description="a Map<String, List<String>> of all user data"), | |||
@ApiResponse(responseCode=SC_OK, description="a Map<String, List<String>> of all user data"), | |||
@ApiResponse(responseCode=SC_FORBIDDEN, description="forbidden if not admin") | |||
} | |||
) | |||
@@ -201,7 +201,7 @@ public class AccountsResource { | |||
description="Update an account. Caller must be the same account, or must be admin.", | |||
parameters={@Parameter(name="id", description="UUID or email of the Account")}, | |||
responses={ | |||
@ApiResponse(responseCode=SC_OK, description="the Account object that was updated", ref="#/components/schemas/Account"), | |||
@ApiResponse(responseCode=SC_OK, description="the Account object that was updated"), | |||
@ApiResponse(responseCode=SC_FORBIDDEN, description="forbidden if caller is not admin and is updating any account Account other than themselves"), | |||
@ApiResponse(responseCode=SC_PRECONDITION_FAILED, description="validation errors occurred") | |||
} | |||
@@ -235,7 +235,7 @@ public class AccountsResource { | |||
description="List all launch statuses for an account. Caller must be the same account, or must be admin.", | |||
parameters={@Parameter(name="id", description="UUID or email of the Account")}, | |||
responses={ | |||
@ApiResponse(description="a List<NodeProgressMeterTick> representing the status of active launch operations"), | |||
@ApiResponse(responseCode=SC_OK, description="a List<NodeProgressMeterTick> representing the status of active launch operations"), | |||
@ApiResponse(responseCode=SC_FORBIDDEN, description="forbidden if caller is not admin and is accessing any account Account other than themselves") | |||
} | |||
) | |||
@@ -261,7 +261,7 @@ public class AccountsResource { | |||
description="View the AccountPolicy for an account. Caller must be the same account, or must be admin.", | |||
parameters={@Parameter(name="id", description="UUID or email of the Account")}, | |||
responses={ | |||
@ApiResponse(description="an AccountPolicy object", ref="#/components/schemas/AccountPolicy"), | |||
@ApiResponse(responseCode=SC_OK, description="an AccountPolicy object"), | |||
@ApiResponse(responseCode=SC_FORBIDDEN, description="forbidden if caller is not admin and is accessing any account Account other than themselves") | |||
} | |||
) | |||
@@ -280,7 +280,7 @@ public class AccountsResource { | |||
description="Update the AccountPolicy for an account. Caller must be the same account, or must be admin.", | |||
parameters={@Parameter(name="id", description="UUID or email of the Account")}, | |||
responses={ | |||
@ApiResponse(description="an AccountPolicy object", ref="#/components/schemas/AccountPolicy"), | |||
@ApiResponse(responseCode=SC_OK, description="an AccountPolicy object"), | |||
@ApiResponse(responseCode=SC_FORBIDDEN, description="forbidden if caller is not admin and is accessing any account Account other than themselves"), | |||
@ApiResponse(responseCode=SC_PRECONDITION_FAILED, description="validation errors occurred") | |||
} | |||
@@ -310,7 +310,7 @@ public class AccountsResource { | |||
description="Create or update an AccountContact in the AccountPolicy. Caller must be the same account, or must be admin.", | |||
parameters={@Parameter(name="id", description="UUID or email of the Account")}, | |||
responses={ | |||
@ApiResponse(description="the AccountContact object that was created or updated", ref="#/components/schemas/AccountPolicy"), | |||
@ApiResponse(responseCode=SC_OK, description="the AccountContact object that was created or updated"), | |||
@ApiResponse(responseCode=SC_FORBIDDEN, description="forbidden if caller is not admin and is accessing any account Account other than themselves"), | |||
@ApiResponse(responseCode=SC_PRECONDITION_FAILED, description="validation errors occurred") | |||
} | |||
@@ -351,7 +351,7 @@ public class AccountsResource { | |||
description="Send verification message for an AccountContact. Caller must be the same account, or must be admin.", | |||
parameters={@Parameter(name="id", description="UUID or email of the Account")}, | |||
responses={ | |||
@ApiResponse(description="the AccountContact object that was created or updated", ref="#/components/schemas/AccountContact"), | |||
@ApiResponse(responseCode=SC_OK, description="the AccountContact object that was created or updated"), | |||
@ApiResponse(responseCode=SC_FORBIDDEN, description="forbidden if caller is not admin and is accessing any account Account other than themselves") | |||
} | |||
) | |||
@@ -382,7 +382,7 @@ public class AccountsResource { | |||
@Parameter(name="info", description="the contact information, for example an email address or phone number") | |||
}, | |||
responses={ | |||
@ApiResponse(description="the AccountContact object", ref="#/components/schemas/AccountContact"), | |||
@ApiResponse(responseCode=SC_OK, description="the AccountContact object"), | |||
@ApiResponse(responseCode=SC_FORBIDDEN, description="forbidden if caller is not admin and is accessing any account Account other than themselves"), | |||
@ApiResponse(responseCode=SC_NOT_FOUND, description="no AccountContact exists with the given type and info") | |||
} | |||
@@ -409,7 +409,7 @@ public class AccountsResource { | |||
@Parameter(name="info", description="the contact information, for example an email address or phone number") | |||
}, | |||
responses={ | |||
@ApiResponse(description="the AccountContact object that was deleted", ref="#/components/schemas/AccountContact"), | |||
@ApiResponse(responseCode=SC_OK, description="the AccountContact object that was deleted"), | |||
@ApiResponse(responseCode=SC_FORBIDDEN, description="forbidden if caller is not admin and is accessing any account Account other than themselves") | |||
} | |||
) | |||
@@ -433,7 +433,7 @@ public class AccountsResource { | |||
description="Delete TOTP authenticator AccountContact from an AccountPolicy. Caller must be the same account, or must be admin.", | |||
parameters={@Parameter(name="id", description="UUID or email of the Account")}, | |||
responses={ | |||
@ApiResponse(description="the AccountPolicy object that was updated", ref="#/components/schemas/AccountPolicy"), | |||
@ApiResponse(responseCode=SC_OK, description="the AccountPolicy object that was updated"), | |||
@ApiResponse(responseCode=SC_FORBIDDEN, description="forbidden if caller is not admin and is accessing any account Account other than themselves") | |||
} | |||
) | |||
@@ -463,7 +463,7 @@ public class AccountsResource { | |||
@Parameter(name="uuid", description="UUID of the AccountContact") | |||
}, | |||
responses={ | |||
@ApiResponse(description="the AccountPolicy object that was updated", ref="#/components/schemas/AccountPolicy"), | |||
@ApiResponse(responseCode=SC_OK, description="the AccountPolicy object that was updated"), | |||
@ApiResponse(responseCode=SC_FORBIDDEN, description="forbidden if caller is not admin and is accessing any account Account other than themselves") | |||
} | |||
) | |||
@@ -486,7 +486,7 @@ public class AccountsResource { | |||
description="Request deletion of an Account. Caller must be the same account, or must be admin.", | |||
parameters={@Parameter(name="id", description="UUID or email of the Account")}, | |||
responses={ | |||
@ApiResponse(description="the AccountMessage object that was sent", ref="#/components/schemas/AccountMessage"), | |||
@ApiResponse(responseCode=SC_OK, description="the AccountMessage object that was sent"), | |||
@ApiResponse(responseCode=SC_FORBIDDEN, description="forbidden if caller is not admin and is accessing any account Account other than themselves") | |||
} | |||
) | |||
@@ -516,7 +516,7 @@ public class AccountsResource { | |||
description="Change password for an account. Caller must be admin.", | |||
parameters={@Parameter(name="id", description="UUID or email of the Account")}, | |||
responses={ | |||
@ApiResponse(description="the Account object that was updated", ref="#/components/schemas/Account"), | |||
@ApiResponse(responseCode=SC_OK, description="the Account object that was updated"), | |||
@ApiResponse(responseCode=SC_FORBIDDEN, description="forbidden if caller is not admin and is accessing any account Account other than themselves"), | |||
@ApiResponse(responseCode=SC_PRECONDITION_FAILED, description="validation errors occurred") | |||
} | |||
@@ -595,7 +595,7 @@ public class AccountsResource { | |||
description="Delete an Account. Caller must be admin.", | |||
parameters={@Parameter(name="id", description="UUID or email of the Account")}, | |||
responses={ | |||
@ApiResponse(description="the Account object that was deleted", ref="#/components/schemas/Account"), | |||
@ApiResponse(responseCode=SC_OK, description="the Account object that was deleted"), | |||
@ApiResponse(responseCode=SC_FORBIDDEN, description="forbidden if caller is not admin and is accessing any account Account other than themselves") | |||
} | |||
) | |||
@@ -634,7 +634,7 @@ public class AccountsResource { | |||
description="Get status of mitmproxy. Caller must be admin.", | |||
parameters={@Parameter(name="id", description="UUID or email of the Account")}, | |||
responses={ | |||
@ApiResponse(description="returns true if mitmproxy is enabled, false otherwise", | |||
@ApiResponse(responseCode=SC_OK, description="returns true if mitmproxy is enabled, false otherwise", | |||
content={@Content(mediaType=APPLICATION_JSON, examples={ | |||
@ExampleObject(name="mitmproxy is enabled", value="true"), | |||
@ExampleObject(name="mitmproxy is disabled", value="false") | |||
@@ -657,7 +657,7 @@ public class AccountsResource { | |||
description="Enable mitmproxy. Caller must be admin.", | |||
parameters={@Parameter(name="id", description="UUID or email of the Account")}, | |||
responses={ | |||
@ApiResponse(description="returns true if mitmproxy is enabled, false otherwise", | |||
@ApiResponse(responseCode=SC_OK, description="returns true if mitmproxy is enabled, false otherwise", | |||
content={@Content(mediaType=APPLICATION_JSON, examples={ | |||
@ExampleObject(name="mitmproxy is enabled", value="true"), | |||
@ExampleObject(name="mitmproxy is disabled", value="false") | |||
@@ -680,7 +680,7 @@ public class AccountsResource { | |||
description="Disable mitmproxy. Caller must be admin.", | |||
parameters={@Parameter(name="id", description="UUID or email of the Account")}, | |||
responses={ | |||
@ApiResponse(description="returns true if mitmproxy is enabled, false otherwise", | |||
@ApiResponse(responseCode=SC_OK, description="returns true if mitmproxy is enabled, false otherwise", | |||
content={@Content(mediaType=APPLICATION_JSON, examples={ | |||
@ExampleObject(name="mitmproxy is enabled", value="true"), | |||
@ExampleObject(name="mitmproxy is disabled", value="false") | |||
@@ -758,7 +758,7 @@ public class AccountsResource { | |||
summary="List all device types", | |||
description="List all device types", | |||
parameters={@Parameter(name="id", description="UUID or email of the Account")}, | |||
responses={@ApiResponse(description="returns an array of Strings, each a BubbleDeviceType enum value")} | |||
responses={@ApiResponse(responseCode=SC_OK, description="returns an array of Strings, each a BubbleDeviceType enum value")} | |||
) | |||
public Response getDeviceTypes(@Context ContainerRequest ctx) { | |||
return ok(BubbleDeviceType.getSelectableTypes()); | |||
@@ -135,7 +135,7 @@ public class AuthResource { | |||
@Operation(tags={API_TAG_UTILITY}, | |||
summary="Read public system configuration", | |||
description="Read public system configuration", | |||
responses={@ApiResponse(description="a Map<String, Object> of public system configuration settings")} | |||
responses={@ApiResponse(responseCode=SC_OK, description="a Map<String, Object> of public system configuration settings")} | |||
) | |||
public Response getPublicSystemConfigs(@Context ContainerRequest ctx) { | |||
return ok(configuration.getPublicSystemConfigs()); | |||
@@ -171,7 +171,7 @@ public class AuthResource { | |||
summary="Determine if the API has been activated", | |||
description="Determine if the API has been activated", | |||
responses={ | |||
@ApiResponse(description="returns true if API is activated, false otherwise", | |||
@ApiResponse(responseCode=SC_OK, description="returns true if API is activated, false otherwise", | |||
content={@Content(mediaType=APPLICATION_JSON, examples={ | |||
@ExampleObject(name="Bubble is activated", value="true"), | |||
@ExampleObject(name="Bubble has not been activated", value="false") | |||
@@ -184,7 +184,7 @@ public class AuthResource { | |||
@Operation(tags={API_TAG_ACTIVATION}, | |||
summary="Get activation default configuration", | |||
description="Get activation default configuration", | |||
responses={@ApiResponse(description="returns an array of CloudService[] representing the default CloudServices and their settings")} | |||
responses={@ApiResponse(responseCode=SC_OK, description="returns an array of CloudService[] representing the default CloudServices and their settings")} | |||
) | |||
public Response getActivationConfigs(@Context ContainerRequest ctx) { | |||
final Account caller = optionalUserPrincipal(ctx); | |||
@@ -198,7 +198,7 @@ public class AuthResource { | |||
summary="Perform one-time activation", | |||
description="Perform one-time activation", | |||
responses={ | |||
@ApiResponse(description="the Account object for the initial admin account, with a new session token"), | |||
@ApiResponse(responseCode=SC_OK, description="the Account object for the initial admin account, with a new session token"), | |||
@ApiResponse(responseCode=SC_FORBIDDEN, description="forbidden if caller is not admin and activation has already been completed"), | |||
@ApiResponse(responseCode=SC_PRECONDITION_FAILED, description="validation errors occurred: activation has already been completed, or there were errors processing the ActivationRequest object") | |||
} | |||
@@ -326,7 +326,7 @@ public class AuthResource { | |||
summary="Register a new Account, starts a new API session.", | |||
description="Register a new Account, starts a new API session.", | |||
responses={ | |||
@ApiResponse(responseCode=SC_OK, description="the Account object that was registered", ref="#/components/schemas/Account"), | |||
@ApiResponse(responseCode=SC_OK, description="the Account object that was registered, `token` property holds session token"), | |||
@ApiResponse(responseCode=SC_PRECONDITION_FAILED, description="validation errors occurred") | |||
} | |||
) | |||
@@ -443,7 +443,7 @@ public class AuthResource { | |||
description="Login an Account, starts a new API session.", | |||
parameters={@Parameter(name="k", description="for a new Bubble that was launched with the lock enabled, the unlock key is required for the first login")}, | |||
responses={ | |||
@ApiResponse(responseCode=SC_OK, description="the Account object that was logged in", ref="#/components/schemas/Account"), | |||
@ApiResponse(responseCode=SC_OK, description="the Account object that was logged in"), | |||
@ApiResponse(responseCode=SC_PRECONDITION_FAILED, description="validation errors occurred") | |||
} | |||
) | |||
@@ -524,7 +524,7 @@ public class AuthResource { | |||
description="Login an Account using an existing session token, starts a new API session. If an existing session exists, it is invalidated", | |||
parameters={@Parameter(name="session", description="the session token to use for logging in")}, | |||
responses={ | |||
@ApiResponse(responseCode=SC_OK, description="the Account object that was logged in", ref="#/components/schemas/Account"), | |||
@ApiResponse(responseCode=SC_OK, description="the Account object that was logged in"), | |||
@ApiResponse(responseCode=SC_PRECONDITION_FAILED, description="validation errors occurred") | |||
} | |||
) | |||
@@ -592,7 +592,7 @@ public class AuthResource { | |||
summary="Called between Bubbles to verify RSA keys", | |||
description="Called between Bubbles to verify RSA keys", | |||
responses={ | |||
@ApiResponse(responseCode=SC_OK, description="an RsaMessage object representing the encrypted challenge response", ref="#/components/schemas/RsaMessage"), | |||
@ApiResponse(responseCode=SC_OK, description="an RsaMessage object representing the encrypted challenge response"), | |||
@ApiResponse(responseCode=SC_NOT_FOUND, description="some error occurred, check response") | |||
} | |||
) | |||
@@ -654,7 +654,7 @@ public class AuthResource { | |||
summary="Re-key a node. Must be admin. Creates a new NodeKey that, being newest, will be the one the node starts using", | |||
description="Re-key a node. Must be admin. Creates a new NodeKey that, being newest, will be the one the node starts using", | |||
responses={ | |||
@ApiResponse(description="the NodeKey that was created", ref="#/components/schemas/BubbleNodeKey"), | |||
@ApiResponse(responseCode=SC_OK, description="the NodeKey that was created"), | |||
@ApiResponse(responseCode=SC_FORBIDDEN, description="forbidden if caller is not admin and is accessing any account Account other than themselves"), | |||
@ApiResponse(responseCode=SC_PRECONDITION_FAILED, description="validation errors occurred") | |||
} | |||
@@ -727,6 +727,19 @@ public class AuthResource { | |||
} | |||
@POST @Path(EP_APPROVE+"/{token}") | |||
@Operation(tags={API_TAG_AUTH}, | |||
summary="Approve a request", | |||
description="Approve a request. The token comes from an email or SMS message sent to the user.", | |||
parameters={@Parameter(name="token", description="the confirmation token")}, | |||
responses={ | |||
@ApiResponse(responseCode=SC_OK, description="HTTP status 200 indicates success", | |||
content={@Content(mediaType=APPLICATION_JSON, examples={ | |||
@ExampleObject(name="if no login requested, returns an empty response", value=""), | |||
@ExampleObject(name="if login requested, returns an Account object with either a valid session token ('token' property) or additional auth factors required (check 'multifactorAuth' property)") | |||
})}), | |||
@ApiResponse(responseCode=SC_PRECONDITION_FAILED, description="a validation error occurred, for example the token might be invalid") | |||
} | |||
) | |||
public Response approve(@Context Request req, | |||
@Context ContainerRequest ctx, | |||
@PathParam("token") String token, | |||
@@ -764,6 +777,18 @@ public class AuthResource { | |||
} | |||
@POST @Path(EP_AUTHENTICATOR) | |||
@Operation(tags={API_TAG_AUTH}, | |||
summary="Approve a TOTP request", | |||
description="Approve a TOTP request. The token comes the end user's authenticator app.", | |||
responses={ | |||
@ApiResponse(responseCode=SC_OK, description="HTTP status 200 indicates success", | |||
content={@Content(mediaType=APPLICATION_JSON, examples={ | |||
@ExampleObject(name="if no login requested, returns an empty response", value=""), | |||
@ExampleObject(name="if login requested, returns an Account object with either a valid session token ('token' property) or additional auth factors required (check 'multifactorAuth' property)") | |||
})}), | |||
@ApiResponse(responseCode=SC_PRECONDITION_FAILED, description="a validation error occurred, for example the token might be invalid") | |||
} | |||
) | |||
public Response authenticator(@Context Request req, | |||
@Context ContainerRequest ctx, | |||
AuthenticatorRequest request) { | |||
@@ -816,15 +841,38 @@ public class AuthResource { | |||
} | |||
@DELETE @Path(EP_AUTHENTICATOR) | |||
@Operation(security=@SecurityRequirement(name=SEC_API_KEY)) | |||
@Operation(security=@SecurityRequirement(name=SEC_API_KEY), | |||
tags={API_TAG_AUTH}, | |||
summary="Flush authenticator tokens", | |||
description="Flush authenticator tokens. The next operation that requires TOTP auth will require the user to re-authenticate.", | |||
responses={ | |||
@ApiResponse(responseCode=SC_OK, description="HTTP status 200 indicates success", | |||
content={@Content(mediaType=APPLICATION_JSON, examples={ | |||
@ExampleObject(name="returns an empty JSON object", value="{}") | |||
})}), | |||
@ApiResponse(responseCode=SC_PRECONDITION_FAILED, description="a validation error occurred, for example the token might be invalid") | |||
} | |||
) | |||
public Response flushAuthenticatorTokens(@Context Request req, | |||
@Context ContainerRequest ctx) { | |||
@Context ContainerRequest ctx) { | |||
final Account caller = userPrincipal(ctx); | |||
authenticatorService.flush(caller.getToken()); | |||
return ok_empty(); | |||
} | |||
@POST @Path(EP_DENY+"/{token}") | |||
@Operation(tags={API_TAG_AUTH}, | |||
summary="Deny a request", | |||
description="Deny a request. The token comes from an email or SMS message sent to the user.", | |||
parameters={@Parameter(name="token", description="the confirmation token")}, | |||
responses={ | |||
@ApiResponse(responseCode=SC_OK, description="HTTP status 200 indicates success", | |||
content={@Content(mediaType=APPLICATION_JSON, examples={ | |||
@ExampleObject(name="returns the denial AccountMessage") | |||
})}), | |||
@ApiResponse(responseCode=SC_PRECONDITION_FAILED, description="a validation error occurred, for example the token might be invalid") | |||
} | |||
) | |||
public Response deny(@Context Request req, | |||
@Context ContainerRequest ctx, | |||
@PathParam("token") String token) { | |||
@@ -838,6 +886,15 @@ public class AuthResource { | |||
@GET @Path(EP_CA_CERT) | |||
@Produces(CONTENT_TYPE_ANY) | |||
@Operation(tags={API_TAG_UTILITY}, | |||
summary="Get the CA Certificate for this Bubble", | |||
description="Get the CA Certificate for this Bubble. Response body is the certificate itself, in a format determined by deviceType or type", | |||
parameters={ | |||
@Parameter(name="deviceType", description="the device type"), | |||
@Parameter(name="type", description="the certificate type") | |||
}, | |||
responses={@ApiResponse(responseCode=SC_OK, description="HTTP status 200 indicates success, response body is certificate")} | |||
) | |||
public Response getCaCert(@Context Request req, | |||
@Context ContainerRequest ctx, | |||
@QueryParam("deviceType") BubbleDeviceType deviceType, | |||
@@ -867,6 +924,13 @@ public class AuthResource { | |||
} | |||
@GET @Path(EP_KEY) | |||
@Operation(tags={API_TAG_UTILITY}, | |||
summary="Get the Node Key for this Bubble", | |||
description="Get the Node Key for this Bubble", | |||
responses={ | |||
@ApiResponse(responseCode=SC_OK, description="HTTP status 200 indicates success") | |||
} | |||
) | |||
public Response getNodeKey(@Context Request req, | |||
@Context ContainerRequest ctx) { | |||
final BubbleNode thisNode = configuration.getThisNode(); | |||
@@ -886,7 +950,16 @@ public class AuthResource { | |||
} | |||
@GET @Path(EP_LOGOUT) | |||
@Operation(security=@SecurityRequirement(name=SEC_API_KEY)) | |||
@Operation(security=@SecurityRequirement(name=SEC_API_KEY), | |||
tags={API_TAG_AUTH}, | |||
summary="Logout", | |||
description="Logout of the current session, or logout of all sessions everywhere if the `all` parameter is true.", | |||
parameters={@Parameter(name="all", description="logout of all sessions everywhere")}, | |||
responses={ | |||
@ApiResponse(responseCode=SC_OK, description="HTTP status 200 indicates success"), | |||
@ApiResponse(responseCode=SC_PRECONDITION_FAILED, description="If there is no current session to log out of") | |||
} | |||
) | |||
public Response logout(@Context ContainerRequest ctx, | |||
@QueryParam("all") Boolean all) { | |||
final Account account = optionalUserPrincipal(ctx); | |||
@@ -900,7 +973,16 @@ public class AuthResource { | |||
} | |||
@POST @Path(EP_LOGOUT+"/{id}") | |||
@Operation(security=@SecurityRequirement(name=SEC_API_KEY)) | |||
@Operation(security=@SecurityRequirement(name=SEC_API_KEY), | |||
tags={API_TAG_AUTH}, | |||
summary="Logout a user everywhere", | |||
description="Logout of the current session, or logout of all sessions everywhere if the `all` parameter is true.", | |||
parameters={@Parameter(name="id", description="UUID or email of user to logout")}, | |||
responses={ | |||
@ApiResponse(responseCode=SC_OK, description="HTTP status 200 indicates success"), | |||
@ApiResponse(responseCode=SC_PRECONDITION_FAILED, description="If there is no current session to log out of") | |||
} | |||
) | |||
public Response logoutUserEverywhere(@Context ContainerRequest ctx, | |||
@PathParam("id") String id) { | |||
final Account caller = optionalUserPrincipal(ctx); | |||
@@ -916,11 +998,27 @@ public class AuthResource { | |||
return ok_empty(); | |||
} | |||
@GET @Path(EP_TIME) public Response serverTime() { return ok(now()); } | |||
@GET @Path(EP_TIME) | |||
@Operation(tags={API_TAG_UTILITY}, | |||
summary="Get current system time", | |||
description="Get current system time. Returns current time as epoch time in milliseconds", | |||
responses={ | |||
@ApiResponse(responseCode=SC_OK, description="Returns current time as epoch time in milliseconds", | |||
content={@Content(mediaType=APPLICATION_JSON, examples={@ExampleObject(name="time in milliseconds", value="1606858589683")})}) | |||
} | |||
) | |||
public Response serverTime() { return ok(now()); } | |||
@Autowired private GeoService geoService; | |||
@GET @Path(EP_SUPPORT) | |||
@Operation(tags={API_TAG_UTILITY}, | |||
summary="Get support information", | |||
description="Get support information for the user's current locale, if available. Use the default locale otherwise.", | |||
responses={ | |||
@ApiResponse(responseCode=SC_OK, description="SupportInfo object") | |||
} | |||
) | |||
public Response getSupportInfo (@Context Request req, | |||
@Context ContainerRequest ctx) { | |||
final List<String> locales = geoService.getSupportedLocales(optionalUserPrincipal(ctx), getRemoteHost(req), normalizeLangHeader(req)); | |||
@@ -928,6 +1026,14 @@ public class AuthResource { | |||
} | |||
@GET @Path(EP_SUPPORT+"/{locale}") | |||
@Operation(tags={API_TAG_UTILITY}, | |||
summary="Get support information", | |||
description="Get support information for the given locale, if available. Use the default locale otherwise.", | |||
parameters={@Parameter(name="locale", description="locale to find support for")}, | |||
responses={ | |||
@ApiResponse(responseCode=SC_OK, description="SupportInfo object") | |||
} | |||
) | |||
public Response getSupportInfo (@Context Request req, | |||
@Context ContainerRequest ctx, | |||
@PathParam("locale") String locale) { | |||
@@ -935,6 +1041,13 @@ public class AuthResource { | |||
} | |||
@GET @Path(EP_APP_LINKS) | |||
@Operation(tags={API_TAG_UTILITY}, | |||
summary="Get links to native applications", | |||
description="Get links to native applications for the current user's locale, if available. Use the default locale otherwise.", | |||
responses={ | |||
@ApiResponse(responseCode=SC_OK, description="AppLinks object") | |||
} | |||
) | |||
public Response getAppLinks (@Context Request req, | |||
@Context ContainerRequest ctx) { | |||
final List<String> locales = geoService.getSupportedLocales(optionalUserPrincipal(ctx), getRemoteHost(req), normalizeLangHeader(req)); | |||
@@ -942,6 +1055,14 @@ public class AuthResource { | |||
} | |||
@GET @Path(EP_APP_LINKS+"/{locale}") | |||
@Operation(tags={API_TAG_UTILITY}, | |||
summary="Get links to native applications", | |||
description="Get links to native applications for the given locale, if available. Use the default locale otherwise.", | |||
parameters={@Parameter(name="locale", description="locale to find app links for")}, | |||
responses={ | |||
@ApiResponse(responseCode=SC_OK, description="AppLinks object") | |||
} | |||
) | |||
public Response getAppLinks (@Context Request req, | |||
@Context ContainerRequest ctx, | |||
@PathParam("locale") String locale) { | |||
@@ -950,6 +1071,15 @@ public class AuthResource { | |||
@GET @Path(EP_PATCH+"/{token}") | |||
@Produces(APPLICATION_OCTET_STREAM) | |||
@Operation(tags={API_TAG_NODE_MANAGER}, | |||
summary="Find a node-manager patch file", | |||
description="Find a node-manager patch file. The file must previously have been registered, yielding a token", | |||
parameters={@Parameter(name="token", description="token of the patch file to retrieve")}, | |||
responses={ | |||
@ApiResponse(responseCode=SC_OK, description="raw file data", content={@Content(mediaType=APPLICATION_OCTET_STREAM)}), | |||
@ApiResponse(responseCode=SC_NOT_FOUND, description="token not valid") | |||
} | |||
) | |||
public Response getPatchFile(@Context ContainerRequest ctx, | |||
@PathParam("token") String token) { | |||
final File patch = nodeManagerService.findPatch(token); | |||
@@ -961,6 +1091,19 @@ public class AuthResource { | |||
@GET @Path(EP_UPGRADE+"/{node}/{key}") | |||
@Produces(APPLICATION_OCTET_STREAM) | |||
@Operation(tags={API_TAG_NODE}, | |||
summary="Return bubble jar", | |||
description="Return bubble jar file for upgrading other nodes to our version.", | |||
parameters={ | |||
@Parameter(name="node", description="UUID of the calling node"), | |||
@Parameter(name="key", description="UUID of the calling node's BubbleNodeKey") | |||
}, | |||
responses={ | |||
@ApiResponse(responseCode=SC_OK, description="raw jar file data", content={@Content(mediaType=APPLICATION_OCTET_STREAM)}), | |||
@ApiResponse(responseCode=SC_UNAUTHORIZED, description="calling node is not authorized"), | |||
@ApiResponse(responseCode=SC_NOT_FOUND, description="token not valid") | |||
} | |||
) | |||
public Response getUpgrade(@Context Request req, | |||
@Context ContainerRequest ctx, | |||
@PathParam("node") String nodeUuid, | |||
@@ -1,18 +0,0 @@ | |||
/** | |||
* Copyright (c) 2020 Bubble, Inc. All rights reserved. | |||
* For personal (non-commercial) use, see license: https://getbubblenow.com/bubble-license/ | |||
*/ | |||
package bubble.server; | |||
import lombok.Getter; | |||
import lombok.Setter; | |||
public class BasicAppLinks { | |||
@Getter @Setter private String ios; | |||
@Getter @Setter private String android; | |||
@Getter @Setter private String windows; | |||
@Getter @Setter private String macosx; | |||
@Getter @Setter private String linux; | |||
} |
@@ -13,6 +13,7 @@ import bubble.dao.account.AccountDAO; | |||
import bubble.dao.bill.AccountPlanDAO; | |||
import bubble.dao.bill.BubblePlanDAO; | |||
import bubble.dao.cloud.CloudServiceDAO; | |||
import bubble.model.AppLinks; | |||
import bubble.model.bill.AccountPlan; | |||
import bubble.model.bill.BubblePlan; | |||
import bubble.model.cloud.BubbleNetwork; | |||
@@ -17,6 +17,7 @@ openApi: | |||
licenseUrl: https://getbubblenow.com/bubble-license/ | |||
additionalPackages: | |||
- org.cobbzilla.wizard.model.search | |||
- org.cobbzilla.wizard.model.support | |||
defaultLocale: {{#exists BUBBLE_DEFAULT_LOCALE}}{{BUBBLE_DEFAULT_LOCALE}}{{else}}en_US{{/exists}} | |||
testMode: {{#exists BUBBLE_TEST_MODE}}{{BUBBLE_TEST_MODE}}{{else}}false{{/exists}} | |||
@@ -1 +1 @@ | |||
Subproject commit c40f0704e1acb17ca3d1f1a4b590fe11997aa1b6 | |||
Subproject commit 09d23d287d18bfa2655f56cb80a3dd7c4bcc0816 |