Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions openespi-common/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,13 @@
<scope>provided</scope>
</dependency>

<!-- Swagger/OpenAPI Annotations for API documentation -->
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-annotations-jakarta</artifactId>
<version>2.2.22</version>
</dependency>

<!-- Flyway and database-specific dependencies moved to application pom.xml files -->

<!-- TestContainers for integration testing -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,16 @@ public class AuthorizationEntity extends IdentifiedObject {
@Column(name = "authorization_uri", length = 512)
private String authorizationURI;

/**
* URI for accessing PII data the Third Party is authorized to access.
* Used in GET of the PII resource subscription.
* Note: This URI will have a different namespace than resourceURI.
*
* @see <a href="https://www.naesb.org/ESPI_Standards.asp">NAESB ESPI 4.0</a>
*/
@Column(name = "customer_resource_uri", length = 512)
private String customerResourceURI;

/**
* Third party identifier.
* Identifies the third-party application or organization.
Expand Down Expand Up @@ -280,51 +290,6 @@ protected String generateDefaultUpHref() {
return getUpHref();
}

/**
* Merges data from another AuthorizationEntity.
* Updates authorization parameters while preserving relationships.
*
* @param other the other authorization entity to merge from
*/
public void merge(AuthorizationEntity other) {
if (other != null) {
super.merge(other);

// Update authorization parameters
this.authorizedPeriod = other.authorizedPeriod;
this.publishedPeriod = other.publishedPeriod;
this.status = other.status;
this.expiresIn = other.expiresIn;
this.grantType = other.grantType;
this.scope = other.scope;
this.responseType = other.responseType;
this.tokenType = other.tokenType;
this.error = other.error;
this.errorDescription = other.errorDescription;
this.errorUri = other.errorUri;
this.resourceURI = other.resourceURI;
this.authorizationURI = other.authorizationURI;
this.thirdParty = other.thirdParty;

// Note: Sensitive fields like tokens are not merged
// Note: Relationships are not merged to preserve existing associations
}
}

/**
* Clears all relationships when unlinking the entity.
* Simplified - applications handle relationship cleanup.
*/
public void unlink() {
clearRelatedLinks();

// Simple field clearing - applications handle bidirectional cleanup
this.retailCustomer = null;
this.subscription = null;

// Note: applicationInformation is not cleared as it might be referenced elsewhere
}

/**
* Checks if this authorization is currently active.
*
Expand Down Expand Up @@ -516,6 +481,7 @@ public String toString() {
"errorUri = " + getErrorUri() + ", " +
"resourceURI = " + getResourceURI() + ", " +
"authorizationURI = " + getAuthorizationURI() + ", " +
"customerResourceURI = " + getCustomerResourceURI() + ", " +
"thirdParty = " + getThirdParty() + ", " +
"description = " + getDescription() + ", " +
"created = " + getCreated() + ", " +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,95 +19,208 @@

package org.greenbuttonalliance.espi.common.dto.usage;

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.xml.bind.annotation.*;

/**
* Authorization DTO record for JAXB XML marshalling/unmarshalling.
*
*
* Represents OAuth 2.0 authorization for third-party access to Green Button data.
* Complies with NAESB ESPI 4.0 XSD specification.
*
* @see <a href="https://www.naesb.org/ESPI_Standards.asp">NAESB ESPI 4.0</a>
*/
@XmlRootElement(name = "Authorization", namespace = "http://naesb.org/espi")
@XmlAccessorType(XmlAccessType.PROPERTY)
@XmlType(name = "Authorization", namespace = "http://naesb.org/espi", propOrder = {
"accessToken", "authorizationUri", "applicationInformationId", "retailCustomerId",
"resourceURI", "scope", "status", "expiresIn", "grantType", "refreshToken",
"tokenType", "thirdParty", "ppid", "authorizationCode"
"authorizedPeriod", // ESPI 4.0 XSD sequence
"publishedPeriod",
"status",
"expiresIn", // Maps to expires_at in XML
"grantType",
"scope",
"tokenType",
"error",
"errorDescription",
"errorUri",
"resourceURI",
"authorizationUri",
"customerResourceURI" // NEW - PII subscription URI
})
public record AuthorizationDto(


// UUID (not in XSD - internal use only)
@XmlTransient
String uuid,
String accessToken,
String authorizationUri,
String applicationInformationId,
String retailCustomerId,
String resourceURI,
String scope,

// XSD-compliant fields (in order)

@Schema(description = "Period during which this authorization is valid",
example = "{\"start\": 1704067200, \"duration\": 31536000}")
DateTimeIntervalDto authorizedPeriod,

@Schema(description = "Period during which data was published",
example = "{\"start\": 1704067200, \"duration\": 31536000}")
DateTimeIntervalDto publishedPeriod,

@Schema(description = "Authorization status (1=ACTIVE, 2=REVOKED, 3=EXPIRED, 4=PENDING)",
example = "1")
Short status,

@Schema(description = "Expiration timestamp (epoch seconds)",
example = "1735689600")
Long expiresIn,

@Schema(description = "OAuth2 grant type",
example = "authorization_code",
allowableValues = {"authorization_code", "client_credentials", "refresh_token"})
String grantType,
String refreshToken,

@Schema(description = "OAuth2 scope defining permissions",
example = "FB=1_3_4_5_13_14_39;IntervalDuration=3600",
required = true)
String scope,

@Schema(description = "OAuth2 token type",
example = "Bearer",
allowableValues = {"Bearer"})
String tokenType,

@Schema(description = "OAuth2 error code if authorization failed",
example = "invalid_grant")
String error,

@Schema(description = "Human-readable error description",
example = "The provided authorization grant is invalid")
String errorDescription,

@Schema(description = "URI with more information about the error",
example = "https://example.com/oauth/errors/invalid_grant")
String errorUri,

@Schema(description = "URI for accessing the authorized energy usage resource",
example = "https://api.example.com/espi/1_1/resource/Batch/Subscription/12345",
required = true)
String resourceURI,

@Schema(description = "URI for managing this authorization",
example = "https://api.example.com/espi/1_1/resource/Authorization/67890",
required = true)
String authorizationUri,

@Schema(description = "URI for accessing PII data the Third Party is authorized to access. Points to PII resource subscription endpoint with different namespace than resourceURI.",
example = "https://api.example.com/customer/espi/1_1/resource/Batch/RetailCustomer/12345")
String customerResourceURI,

// OAuth2 implementation fields (not in ESPI XSD - marked as @XmlTransient)

@XmlTransient
@Schema(description = "OAuth2 access token (not included in XML for security)",
example = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...")
String accessToken,

@XmlTransient
@Schema(description = "OAuth2 refresh token (not included in XML for security)",
example = "def50200...")
String refreshToken,

@XmlTransient
@Schema(description = "OAuth2 authorization code (temporary, not in XML)",
example = "abc123xyz")
String authorizationCode,

@XmlTransient
@Schema(description = "OAuth2 state parameter for CSRF protection (not in ESPI XSD)",
example = "xyz789")
String state,

@XmlTransient
@Schema(description = "OAuth2 response type (not in ESPI XSD)",
example = "code")
String responseType,

@XmlTransient
@Schema(description = "Third party application identifier (not in ESPI XSD)",
example = "ThirdPartyApp")
String thirdParty,
String ppid,
String authorizationCode

@XmlTransient
@Schema(description = "Application information ID (relationship, not in XSD)",
example = "550e8400-e29b-41d4-a716-446655440000")
String applicationInformationId,

@XmlTransient
@Schema(description = "Retail customer ID (relationship, not in XSD for privacy)",
example = "660e8400-e29b-41d4-a716-446655440000")
String retailCustomerId
) {

/**
* Default constructor for JAXB.
*/
public AuthorizationDto() {
this(null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null);
this(null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null);
}

/**
* Constructor for basic authorization.
* Constructor for basic authorization (XSD-compliant fields only).
*/
public AuthorizationDto(String accessToken, String scope, String retailCustomerId) {
this(null, accessToken, null, null, retailCustomerId, null, scope, null, null,
null, null, null, null, null, null);
public AuthorizationDto(String scope, Short status, String resourceURI, String authorizationUri) {
this(null, null, null, status, null, null, scope, null, null, null, null,
resourceURI, authorizationUri, null, null, null, null, null, null, null, null, null);
}

// JAXB property accessors
@XmlElement(name = "accessToken", namespace = "http://naesb.org/espi")
public String getAccessToken() { return accessToken; }

@XmlElement(name = "authorizationURI", namespace = "http://naesb.org/espi")
public String getAuthorizationUri() { return authorizationUri; }

@XmlElement(name = "applicationInformationId", namespace = "http://naesb.org/espi")
public String getApplicationInformationId() { return applicationInformationId; }

@XmlElement(name = "retailCustomerId", namespace = "http://naesb.org/espi")
public String getRetailCustomerId() { return retailCustomerId; }

@XmlElement(name = "resourceURI", namespace = "http://naesb.org/espi")
public String getResourceURI() { return resourceURI; }

@XmlElement(name = "scope", namespace = "http://naesb.org/espi")
public String getScope() { return scope; }

@XmlElement(name = "status", namespace = "http://naesb.org/espi")

// JAXB property accessors for XSD-compliant fields (in propOrder sequence)

@XmlElement(name = "authorizedPeriod", namespace = "http://naesb.org/espi")
public DateTimeIntervalDto getAuthorizedPeriod() { return authorizedPeriod; }

@XmlElement(name = "publishedPeriod", namespace = "http://naesb.org/espi")
public DateTimeIntervalDto getPublishedPeriod() { return publishedPeriod; }

@XmlElement(name = "status", namespace = "http://naesb.org/espi", required = true)
public Short getStatus() { return status; }
@XmlElement(name = "expires_in", namespace = "http://naesb.org/espi")

@XmlElement(name = "expires_at", namespace = "http://naesb.org/espi", required = true)
public Long getExpiresIn() { return expiresIn; }

@XmlElement(name = "grant_type", namespace = "http://naesb.org/espi")
public String getGrantType() { return grantType; }
@XmlElement(name = "refresh_token", namespace = "http://naesb.org/espi")
public String getRefreshToken() { return refreshToken; }
@XmlElement(name = "token_type", namespace = "http://naesb.org/espi")

@XmlElement(name = "scope", namespace = "http://naesb.org/espi", required = true)
public String getScope() { return scope; }

@XmlElement(name = "token_type", namespace = "http://naesb.org/espi", required = true)
public String getTokenType() { return tokenType; }

@XmlElement(name = "third_party", namespace = "http://naesb.org/espi")
public String getThirdParty() { return thirdParty; }

@XmlElement(name = "ppid", namespace = "http://naesb.org/espi")
public String getPpid() { return ppid; }

@XmlElement(name = "code", namespace = "http://naesb.org/espi")

@XmlElement(name = "error", namespace = "http://naesb.org/espi")
public String getError() { return error; }

@XmlElement(name = "error_description", namespace = "http://naesb.org/espi")
public String getErrorDescription() { return errorDescription; }

@XmlElement(name = "error_uri", namespace = "http://naesb.org/espi")
public String getErrorUri() { return errorUri; }

@XmlElement(name = "resourceURI", namespace = "http://naesb.org/espi", required = true)
public String getResourceURI() { return resourceURI; }

@XmlElement(name = "authorizationURI", namespace = "http://naesb.org/espi", required = true)
public String getAuthorizationUri() { return authorizationUri; }

@XmlElement(name = "customerResourceURI", namespace = "http://naesb.org/espi")
public String getCustomerResourceURI() { return customerResourceURI; }

// OAuth2 implementation field accessors (marked @XmlTransient, not in XML output)

public String getUuid() { return uuid; }
public String getAccessToken() { return accessToken; }
public String getRefreshToken() { return refreshToken; }
public String getAuthorizationCode() { return authorizationCode; }
public String getState() { return state; }
public String getResponseType() { return responseType; }
public String getThirdParty() { return thirdParty; }
public String getApplicationInformationId() { return applicationInformationId; }
public String getRetailCustomerId() { return retailCustomerId; }
}
Loading
Loading