Skip to content
This repository was archived by the owner on Apr 1, 2026. It is now read-only.

Commit edc7ee0

Browse files
committed
remove http basic auth, establish openapi config modification hook.
1 parent 8eefd13 commit edc7ee0

6 files changed

Lines changed: 106 additions & 136 deletions

File tree

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package org.opendcs.odcsapi.openapi;
2+
3+
import org.slf4j.Logger;
4+
import org.slf4j.LoggerFactory;
5+
6+
import io.swagger.v3.jaxrs2.ReaderListener;
7+
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
8+
import io.swagger.v3.oas.integration.api.OpenApiReader;
9+
import io.swagger.v3.oas.models.OpenAPI;
10+
11+
@OpenAPIDefinition
12+
public class OpenDcsOpenApiModifier implements ReaderListener
13+
{
14+
private static final Logger log = LoggerFactory.getLogger(OpenDcsOpenApiModifier.class);
15+
@Override
16+
public void beforeScan(OpenApiReader reader, OpenAPI openAPI)
17+
{
18+
/* do nothing */
19+
}
20+
21+
@Override
22+
public void afterScan(OpenApiReader reader, OpenAPI openAPI)
23+
{
24+
/**
25+
* todo:
26+
* 1 update authcheck provider to return security scheme with runtime determined info
27+
* 2 add SecuritySchemes
28+
*/
29+
}
30+
31+
}

opendcs-rest-api/src/main/java/org/opendcs/odcsapi/sec/basicauth/BasicAuthResource.java

Lines changed: 71 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,69 @@ public final class BasicAuthResource extends OpenDcsResource
7878
@Context
7979
private HttpHeaders httpHeaders;
8080

81+
@POST
82+
@Path("credentials")
83+
@Consumes(MediaType.APPLICATION_JSON)
84+
@Produces(MediaType.APPLICATION_JSON)
85+
@RolesAllowed({ApiConstants.ODCS_API_GUEST})
86+
@Operation(
87+
summary = "The ‘credentials’ POST method is used to obtain a new token",
88+
description = "The user name and password provided must be a valid login for the underlying database. \n"
89+
+ "Also, that user must be assigned either of the roles OTSDB_ADMIN or OTSDB_MGR.\n"
90+
+ "--- \n\n\n"
91+
+ "Starting in **API Version 0.0.3**, authentication credentials (username and password) "
92+
+ "may be passed as shown above in the POST body. \n"
93+
+ "They may also be passed in a GET call to the 'credentials' method, "
94+
+ "(e.g. '*http://localhost:8080/odcsapi/credentials*') containing an HTTP Authentication Basic "
95+
+ "header in the form 'username:password'. \n\nThe returned data to the GET call will be empty.",
96+
requestBody = @RequestBody(
97+
description = "Login Credentials",
98+
required = true,
99+
content = @Content(
100+
mediaType = MediaType.APPLICATION_JSON,
101+
schema = @Schema(implementation = Credentials.class)
102+
)
103+
),
104+
responses = {
105+
@ApiResponse(
106+
responseCode = "200",
107+
description = "Successful authentication."
108+
),
109+
@ApiResponse(
110+
responseCode = "400",
111+
description = "Bad request - null or otherwise invalid credentials.",
112+
content = @Content(mediaType = MediaType.APPLICATION_JSON,
113+
schema = @Schema(type = "object", implementation = StringToClassMapItem.class),
114+
examples = @ExampleObject(value = "{\"status\":400," +
115+
"\"message\": \"Neither username nor password may be null.\"}"))
116+
),
117+
@ApiResponse(
118+
responseCode = "403",
119+
description = "Invalid credentials or insufficient role.",
120+
content = @Content(mediaType = MediaType.APPLICATION_JSON,
121+
schema = @Schema(type = "object", implementation = StringToClassMapItem.class),
122+
examples = @ExampleObject(value = "{\"status\":403," +
123+
"\"message\":\"Failed to authorize user.\"}"))
124+
),
125+
@ApiResponse(
126+
responseCode = "500",
127+
description = "Internal Server Error"
128+
),
129+
@ApiResponse(
130+
responseCode = "501",
131+
description = "This authentication method is only supported by the OpenTSDB database.",
132+
content = @Content(mediaType = MediaType.APPLICATION_JSON,
133+
schema = @Schema(type = "object", implementation = StringToClassMapItem.class),
134+
examples = @ExampleObject(value = "{\"status\":501," +
135+
"\"message\":\"Basic Auth is not supported.\"}"))
136+
)
137+
}
138+
)
139+
public Response postCredentials(Credentials credentials) throws WebAppException
140+
{
141+
return doLogin(credentials);
142+
}
143+
81144
@POST
82145
@Path("credentials")
83146
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@@ -142,20 +205,18 @@ public Response postCredentials(@FormParam("username") String username,
142205
Credentials credentials = new Credentials();
143206
credentials.setUsername(username);
144207
credentials.setPassword(password);
145-
TimeSeriesDb db = getLegacyTimeseriesDB();
146-
if(!db.isOpenTSDB())
147-
{
148-
throw new ServerErrorException("Basic Auth is not supported", Response.Status.NOT_IMPLEMENTED);
149-
}
208+
return doLogin(credentials);
209+
}
150210

211+
private Response doLogin(Credentials credentials) throws WebAppException
212+
{
151213
//If credentials are null, Authorization header will be checked.
152-
if(credentials != null)
214+
if(credentials == null)
153215
{
154-
verifyCredentials(credentials);
216+
throw newAuthException();
155217
}
156-
157-
String authorizationHeader = httpHeaders.getHeaderString(HttpHeaders.AUTHORIZATION);
158-
credentials = getCredentials(credentials, authorizationHeader);
218+
219+
verifyCredentials(credentials);
159220
validateDbCredentials(credentials);
160221
Set<OpenDcsApiRoles> roles = getUserRoles(credentials.getUsername());
161222
OpenDcsPrincipal principal = new OpenDcsPrincipal(credentials.getUsername(), roles);
@@ -196,75 +257,11 @@ private static void verifyCredentials(Credentials credentials)
196257
}
197258
}
198259

199-
private static Credentials getCredentials(Credentials postBody, String authorizationHeader)
200-
{
201-
if(postBody != null)
202-
{
203-
return postBody;
204-
}
205-
if(authorizationHeader == null || authorizationHeader.isEmpty())
206-
{
207-
throw newAuthException();
208-
}
209-
return parseAuthorizationHeader(authorizationHeader);
210-
}
211-
212260
private static BadRequestException newAuthException()
213261
{
214262
return new BadRequestException("Credentials not provided.");
215263
}
216264

217-
private static Credentials parseAuthorizationHeader(String authString)
218-
{
219-
String[] authHeaders = authString.split(",");
220-
for(String header : authHeaders)
221-
{
222-
String trimmedHeader = header.trim();
223-
LOGGER.debug(MODULE + ".makeToken authHdr = {}", trimmedHeader);
224-
if(trimmedHeader.startsWith("Basic"))
225-
{
226-
return extractCredentials(trimmedHeader.substring(6).trim());
227-
}
228-
}
229-
throw newAuthException();
230-
}
231-
232-
private static Credentials extractCredentials(String base64Credentials)
233-
{
234-
String decodedCredentials = new String(Base64.getDecoder().decode(base64Credentials.getBytes()));
235-
String[] parts = decodedCredentials.split(":", 2);
236-
237-
if(parts.length < 2 || parts[0] == null || parts[1] == null
238-
|| parts[0].isEmpty() || parts[1].isEmpty())
239-
{
240-
throw newAuthException();
241-
}
242-
243-
Credentials credentials = new Credentials();
244-
credentials.setUsername(parts[0]);
245-
credentials.setPassword(parts[1]);
246-
247-
LOGGER.info(MODULE + ".checkToken found tokstr in header.");
248-
return credentials;
249-
}
250-
251-
private String getDatabaseUrl() throws WebAppException
252-
{
253-
DataSource dataSource = (DataSource) context.getAttribute(DATA_SOURCE_ATTRIBUTE_KEY);
254-
try(Connection poolCon = dataSource.getConnection())
255-
{
256-
// The only way to verify that user/pw is valid is to attempt to establish a connection:
257-
// This should eventually be moved to a users table
258-
DatabaseMetaData metaData = poolCon.getMetaData();
259-
return metaData.getURL();
260-
}
261-
catch(SQLException e)
262-
{
263-
throw new WebAppException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
264-
"Failed to obtain database URL.", e);
265-
}
266-
}
267-
268265
private void validateDbCredentials(Credentials creds) throws WebAppException
269266
{
270267

opendcs-rest-api/src/main/java/org/opendcs/odcsapi/sec/basicauth/CredentalReader.java

Lines changed: 0 additions & 33 deletions
This file was deleted.

opendcs-rest-api/src/main/java/org/opendcs/odcsapi/sec/basicauth/LoginProviderResource.java

Lines changed: 0 additions & 14 deletions
This file was deleted.

opendcs-rest-api/src/main/java/org/opendcs/odcsapi/sec/openid/OidcAuthCheck.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import com.nimbusds.jose.proc.BadJOSEException;
3434
import com.nimbusds.jose.proc.SecurityContext;
3535
import com.nimbusds.jwt.JWTClaimsSet;
36+
3637
import org.opendcs.odcsapi.dao.ApiAuthorizationDAI;
3738
import org.opendcs.odcsapi.hydrojson.DbInterface;
3839
import org.opendcs.odcsapi.sec.AuthorizationCheck;

opendcs-rest-api/src/main/webapp/WEB-INF/web.xml

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@
3434
<param-name>jersey.config.server.provider.packages</param-name>
3535
<param-value>
3636
io.swagger.jaxrs.listing,
37-
org.opendcs.odcsapi.res
37+
org.opendcs.odcsapi.res,
38+
org.opendcs.odcsapi.sec,
39+
org.opendcs.odcsapi.openapi
3840
</param-value>
3941
</init-param>
4042
<load-on-startup>1</load-on-startup>
@@ -50,20 +52,6 @@
5052
<param-name>opendcs.rest.api.authorization.expiration.duration</param-name>
5153
<param-value>PT15M</param-value>
5254
</context-param>
53-
<!--
54-
<context-param>
55-
<param-name>opendcs.rest.api.cwms.office</param-name>
56-
<param-value></param-value>
57-
</context-param>
58-
<context-param>
59-
<param-name>opendcs.rest.api.authorization.jwt.jwkset.url</param-name>
60-
<param-value></param-value>
61-
</context-param>
62-
<context-param>
63-
<param-name>opendcs.rest.api.authorization.jwt.issuer.url</param-name>
64-
<param-value></param-value>
65-
</context-param>
66-
-->
6755
<servlet-mapping>
6856
<servlet-name>jersey-servlet</servlet-name>
6957
<url-pattern>/*</url-pattern>

0 commit comments

Comments
 (0)