diff --git a/src/main/java/eu/roboflax/cloudflare/constants/Category.java b/src/main/java/eu/roboflax/cloudflare/constants/Category.java
index 26945cb..914fa1c 100644
--- a/src/main/java/eu/roboflax/cloudflare/constants/Category.java
+++ b/src/main/java/eu/roboflax/cloudflare/constants/Category.java
@@ -393,10 +393,39 @@ public enum Category {
LIST_WORKER_ROUTES( GET, "zones/{id-1}/workers/routes" ),
GET_WORKER_ROUTE( GET, "zones/{id-1}/workers/routes/{id-2}" ),
UPDATE_WORKER_ROUTE( PUT, "zones/{id-1}/workers/routes/{id-2}" ),
- DELETE_WORKER_ROUTE( DELETE, "zones/{id-1}/workers/routes/{id-2}" );
+ DELETE_WORKER_ROUTE( DELETE, "zones/{id-1}/workers/routes/{id-2}" ),
- private HttpMethod httpMethod;
- private String additionalPath;
+ // Cloudflare Tunnel (Zero Trust)
+ LIST_TUNNELS( GET, "accounts/{id-1}/cfd_tunnel" ),
+ CREATE_TUNNEL( POST, "accounts/{id-1}/cfd_tunnel" ),
+ TUNNEL_DETAILS( GET, "accounts/{id-1}/cfd_tunnel/{id-2}" ),
+ UPDATE_TUNNEL( PATCH, "accounts/{id-1}/cfd_tunnel/{id-2}" ),
+ DELETE_TUNNEL( DELETE, "accounts/{id-1}/cfd_tunnel/{id-2}" ),
+ GET_TUNNEL_CONFIGURATION( GET, "accounts/{id-1}/cfd_tunnel/{id-2}/configurations" ),
+ UPDATE_TUNNEL_CONFIGURATION( PUT, "accounts/{id-1}/cfd_tunnel/{id-2}/configurations" ),
+ GET_TUNNEL_TOKEN( GET, "accounts/{id-1}/cfd_tunnel/{id-2}/token" ),
+ LIST_TUNNEL_CONNECTIONS( GET, "accounts/{id-1}/cfd_tunnel/{id-2}/connections" ),
+ DELETE_TUNNEL_CONNECTIONS( DELETE, "accounts/{id-1}/cfd_tunnel/{id-2}/connections" ),
+
+ // Access Service Tokens (Zero Trust)
+ LIST_ACCESS_SERVICE_TOKENS( GET, "accounts/{id-1}/access/service_tokens" ),
+ CREATE_ACCESS_SERVICE_TOKEN( POST, "accounts/{id-1}/access/service_tokens" ),
+ ACCESS_SERVICE_TOKEN_DETAILS( GET, "accounts/{id-1}/access/service_tokens/{id-2}" ),
+ UPDATE_ACCESS_SERVICE_TOKEN( PUT, "accounts/{id-1}/access/service_tokens/{id-2}" ),
+ DELETE_ACCESS_SERVICE_TOKEN( DELETE, "accounts/{id-1}/access/service_tokens/{id-2}" ),
+ REFRESH_ACCESS_SERVICE_TOKEN( POST, "accounts/{id-1}/access/service_tokens/{id-2}/refresh" ),
+ ROTATE_ACCESS_SERVICE_TOKEN( POST, "accounts/{id-1}/access/service_tokens/{id-2}/rotate" ),
+
+ // Access Applications (Zero Trust)
+ LIST_ACCESS_APPLICATIONS( GET, "accounts/{id-1}/access/apps" ),
+ CREATE_ACCESS_APPLICATION( POST, "accounts/{id-1}/access/apps" ),
+ ACCESS_APPLICATION_DETAILS( GET, "accounts/{id-1}/access/apps/{id-2}" ),
+ UPDATE_ACCESS_APPLICATION( PUT, "accounts/{id-1}/access/apps/{id-2}" ),
+ DELETE_ACCESS_APPLICATION( DELETE, "accounts/{id-1}/access/apps/{id-2}" ),
+ REVOKE_ACCESS_APPLICATION_TOKENS( POST, "accounts/{id-1}/access/apps/{id-2}/revoke_tokens" );
+
+ private final HttpMethod httpMethod;
+ private final String additionalPath;
Category( HttpMethod httpMethod, String additionalPath ) {
this.httpMethod = httpMethod;
diff --git a/src/main/java/eu/roboflax/cloudflare/objects/access/AccessApplication.java b/src/main/java/eu/roboflax/cloudflare/objects/access/AccessApplication.java
new file mode 100644
index 0000000..642f9da
--- /dev/null
+++ b/src/main/java/eu/roboflax/cloudflare/objects/access/AccessApplication.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) RoboFlax. All rights reserved.
+ * Use is subject to license terms.
+ */
+package eu.roboflax.cloudflare.objects.access;
+
+import com.google.gson.annotations.Expose;
+import com.google.gson.annotations.SerializedName;
+import eu.roboflax.cloudflare.objects.Identifiable;
+import lombok.Getter;
+import lombok.Setter;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+
+import java.util.List;
+
+/**
+ * Represents a Cloudflare Access Application (Zero Trust).
+ * Access applications define the resources protected by Cloudflare Access.
+ *
+ * @see Cloudflare API
+ */
+@Getter
+@Setter
+public class AccessApplication implements Identifiable {
+
+ @SerializedName("id")
+ @Expose
+ private String id;
+
+ @SerializedName("aud")
+ @Expose
+ private String aud;
+
+ @SerializedName("name")
+ @Expose
+ private String name;
+
+ @SerializedName("domain")
+ @Expose
+ private String domain;
+
+ @SerializedName("type")
+ @Expose
+ private String type;
+
+ @SerializedName("session_duration")
+ @Expose
+ private String sessionDuration;
+
+ @SerializedName("allowed_idps")
+ @Expose
+ private List allowedIdps;
+
+ @SerializedName("auto_redirect_to_identity")
+ @Expose
+ private Boolean autoRedirectToIdentity;
+
+ @SerializedName("enable_binding_cookie")
+ @Expose
+ private Boolean enableBindingCookie;
+
+ @SerializedName("http_only_cookie_attribute")
+ @Expose
+ private Boolean httpOnlyCookieAttribute;
+
+ @SerializedName("same_site_cookie_attribute")
+ @Expose
+ private String sameSiteCookieAttribute;
+
+ @SerializedName("logo_url")
+ @Expose
+ private String logoUrl;
+
+ @SerializedName("skip_interstitial")
+ @Expose
+ private Boolean skipInterstitial;
+
+ @SerializedName("app_launcher_visible")
+ @Expose
+ private Boolean appLauncherVisible;
+
+ @SerializedName("service_auth_401_redirect")
+ @Expose
+ private Boolean serviceAuth401Redirect;
+
+ @SerializedName("custom_deny_message")
+ @Expose
+ private String customDenyMessage;
+
+ @SerializedName("custom_deny_url")
+ @Expose
+ private String customDenyUrl;
+
+ @SerializedName("custom_non_identity_deny_url")
+ @Expose
+ private String customNonIdentityDenyUrl;
+
+ @SerializedName("tags")
+ @Expose
+ private List tags;
+
+ @SerializedName("cors_headers")
+ @Expose
+ private CorsHeaders corsHeaders;
+
+ @SerializedName("created_at")
+ @Expose
+ private String createdAt;
+
+ @SerializedName("updated_at")
+ @Expose
+ private String updatedAt;
+
+ @Getter
+ @Setter
+ public static class CorsHeaders {
+ @SerializedName("allowed_methods")
+ @Expose
+ private List allowedMethods;
+
+ @SerializedName("allowed_origins")
+ @Expose
+ private List allowedOrigins;
+
+ @SerializedName("allowed_headers")
+ @Expose
+ private List allowedHeaders;
+
+ @SerializedName("allow_all_methods")
+ @Expose
+ private Boolean allowAllMethods;
+
+ @SerializedName("allow_all_origins")
+ @Expose
+ private Boolean allowAllOrigins;
+
+ @SerializedName("allow_all_headers")
+ @Expose
+ private Boolean allowAllHeaders;
+
+ @SerializedName("allow_credentials")
+ @Expose
+ private Boolean allowCredentials;
+
+ @SerializedName("max_age")
+ @Expose
+ private Integer maxAge;
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .append("id", id)
+ .append("name", name)
+ .append("domain", domain)
+ .append("type", type)
+ .append("sessionDuration", sessionDuration)
+ .append("createdAt", createdAt)
+ .toString();
+ }
+}
diff --git a/src/main/java/eu/roboflax/cloudflare/objects/access/AccessServiceToken.java b/src/main/java/eu/roboflax/cloudflare/objects/access/AccessServiceToken.java
new file mode 100644
index 0000000..4b8c351
--- /dev/null
+++ b/src/main/java/eu/roboflax/cloudflare/objects/access/AccessServiceToken.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) RoboFlax. All rights reserved.
+ * Use is subject to license terms.
+ */
+package eu.roboflax.cloudflare.objects.access;
+
+import com.google.gson.annotations.Expose;
+import com.google.gson.annotations.SerializedName;
+import eu.roboflax.cloudflare.objects.Identifiable;
+import lombok.Getter;
+import lombok.Setter;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+
+/**
+ * Represents a Cloudflare Access Service Token (Zero Trust).
+ * Service tokens allow automated systems to reach applications protected by Access.
+ *
+ * @see Cloudflare API
+ */
+@Getter
+@Setter
+public class AccessServiceToken implements Identifiable {
+
+ @SerializedName("id")
+ @Expose
+ private String id;
+
+ @SerializedName("name")
+ @Expose
+ private String name;
+
+ @SerializedName("client_id")
+ @Expose
+ private String clientId;
+
+ /**
+ * Only returned when creating or rotating a token.
+ * Store this securely - it cannot be retrieved again.
+ */
+ @SerializedName("client_secret")
+ @Expose
+ private String clientSecret;
+
+ @SerializedName("created_at")
+ @Expose
+ private String createdAt;
+
+ @SerializedName("updated_at")
+ @Expose
+ private String updatedAt;
+
+ @SerializedName("expires_at")
+ @Expose
+ private String expiresAt;
+
+ @SerializedName("duration")
+ @Expose
+ private String duration;
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .append("id", id)
+ .append("name", name)
+ .append("clientId", clientId)
+ .append("createdAt", createdAt)
+ .append("expiresAt", expiresAt)
+ .append("duration", duration)
+ .toString();
+ }
+}
diff --git a/src/main/java/eu/roboflax/cloudflare/objects/tunnel/Tunnel.java b/src/main/java/eu/roboflax/cloudflare/objects/tunnel/Tunnel.java
new file mode 100644
index 0000000..c15167d
--- /dev/null
+++ b/src/main/java/eu/roboflax/cloudflare/objects/tunnel/Tunnel.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) RoboFlax. All rights reserved.
+ * Use is subject to license terms.
+ */
+package eu.roboflax.cloudflare.objects.tunnel;
+
+import com.google.gson.annotations.Expose;
+import com.google.gson.annotations.SerializedName;
+import eu.roboflax.cloudflare.objects.Identifiable;
+import lombok.Getter;
+import lombok.Setter;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+
+import java.util.List;
+
+/**
+ * Represents a Cloudflare Tunnel (Zero Trust).
+ *
+ * @see Cloudflare API
+ */
+@Getter
+@Setter
+public class Tunnel implements Identifiable {
+
+ @SerializedName("id")
+ @Expose
+ private String id;
+
+ @SerializedName("account_tag")
+ @Expose
+ private String accountTag;
+
+ @SerializedName("name")
+ @Expose
+ private String name;
+
+ @SerializedName("status")
+ @Expose
+ private String status;
+
+ @SerializedName("created_at")
+ @Expose
+ private String createdAt;
+
+ @SerializedName("deleted_at")
+ @Expose
+ private String deletedAt;
+
+ @SerializedName("conns_active_at")
+ @Expose
+ private String connsActiveAt;
+
+ @SerializedName("conns_inactive_at")
+ @Expose
+ private String connsInactiveAt;
+
+ @SerializedName("tun_type")
+ @Expose
+ private String tunType;
+
+ @SerializedName("metadata")
+ @Expose
+ private Object metadata;
+
+ @SerializedName("connections")
+ @Expose
+ private List connections;
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .append("id", id)
+ .append("accountTag", accountTag)
+ .append("name", name)
+ .append("status", status)
+ .append("createdAt", createdAt)
+ .append("tunType", tunType)
+ .toString();
+ }
+}
diff --git a/src/main/java/eu/roboflax/cloudflare/objects/tunnel/TunnelConfiguration.java b/src/main/java/eu/roboflax/cloudflare/objects/tunnel/TunnelConfiguration.java
new file mode 100644
index 0000000..908eec5
--- /dev/null
+++ b/src/main/java/eu/roboflax/cloudflare/objects/tunnel/TunnelConfiguration.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) RoboFlax. All rights reserved.
+ * Use is subject to license terms.
+ */
+package eu.roboflax.cloudflare.objects.tunnel;
+
+import com.google.gson.annotations.Expose;
+import com.google.gson.annotations.SerializedName;
+import lombok.Getter;
+import lombok.Setter;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+
+import java.util.List;
+
+/**
+ * Represents a Cloudflare Tunnel Configuration.
+ */
+@Getter
+@Setter
+public class TunnelConfiguration {
+
+ @SerializedName("tunnel_id")
+ @Expose
+ private String tunnelId;
+
+ @SerializedName("version")
+ @Expose
+ private Integer version;
+
+ @SerializedName("config")
+ @Expose
+ private Config config;
+
+ @Getter
+ @Setter
+ public static class Config {
+ @SerializedName("ingress")
+ @Expose
+ private List ingress;
+
+ @SerializedName("warp-routing")
+ @Expose
+ private WarpRouting warpRouting;
+ }
+
+ @Getter
+ @Setter
+ public static class IngressRule {
+ @SerializedName("hostname")
+ @Expose
+ private String hostname;
+
+ @SerializedName("service")
+ @Expose
+ private String service;
+
+ @SerializedName("path")
+ @Expose
+ private String path;
+ }
+
+ @Getter
+ @Setter
+ public static class WarpRouting {
+ @SerializedName("enabled")
+ @Expose
+ private Boolean enabled;
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .append("tunnelId", tunnelId)
+ .append("version", version)
+ .toString();
+ }
+}
diff --git a/src/main/java/eu/roboflax/cloudflare/objects/tunnel/TunnelConnection.java b/src/main/java/eu/roboflax/cloudflare/objects/tunnel/TunnelConnection.java
new file mode 100644
index 0000000..bbdea09
--- /dev/null
+++ b/src/main/java/eu/roboflax/cloudflare/objects/tunnel/TunnelConnection.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) RoboFlax. All rights reserved.
+ * Use is subject to license terms.
+ */
+package eu.roboflax.cloudflare.objects.tunnel;
+
+import com.google.gson.annotations.Expose;
+import com.google.gson.annotations.SerializedName;
+import eu.roboflax.cloudflare.objects.Identifiable;
+import lombok.Getter;
+import lombok.Setter;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+
+/**
+ * Represents a Cloudflare Tunnel Connection.
+ */
+@Getter
+@Setter
+public class TunnelConnection implements Identifiable {
+
+ @SerializedName("id")
+ @Expose
+ private String id;
+
+ @SerializedName("colo_name")
+ @Expose
+ private String coloName;
+
+ @SerializedName("is_pending_reconnect")
+ @Expose
+ private Boolean isPendingReconnect;
+
+ @SerializedName("client_id")
+ @Expose
+ private String clientId;
+
+ @SerializedName("client_version")
+ @Expose
+ private String clientVersion;
+
+ @SerializedName("opened_at")
+ @Expose
+ private String openedAt;
+
+ @SerializedName("origin_ip")
+ @Expose
+ private String originIp;
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .append("id", id)
+ .append("coloName", coloName)
+ .append("clientVersion", clientVersion)
+ .append("openedAt", openedAt)
+ .toString();
+ }
+}