Skip to content

Commit 5b125c1

Browse files
committed
feat: add api call to liferay
1 parent a6ceac8 commit 5b125c1

File tree

13 files changed

+276
-46
lines changed

13 files changed

+276
-46
lines changed

sample-etc-bun/client-extension.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ sample-etc-bun-oauth-application-user-agent:
33
.serviceScheme: http
44
name: Sample Etc Bun OAuth Application User Agent
55
scopes:
6-
- Liferay.Headless.Admin.User.read
6+
- Liferay.Headless.Admin.User.everything.read
77
type: oAuthApplicationUserAgent
88
sample-etc-bun-object-action-1:
99
name: Sample Etc Golang Object Action 1

sample-etc-bun/src/index.ts

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import {log} from "./log.ts";
33
import {type JSONWebKeySet, type JWK, type JWTPayload, jwtVerify} from "jose";
44
import {getConfigMap} from "./config.ts";
55

6-
const configMap = await getConfigMap()
6+
const configMap = await getConfigMap()
77
const externalReferenceCode = configMap.get("liferay.oauth.application.external.reference.codes")?.split(",")[0]
88
const liferayHost = `${configMap.get("com.liferay.lxc.dxp.server.protocol")}://${configMap.get("com.liferay.lxc.dxp.mainDomain")}`
99

@@ -13,37 +13,73 @@ const AuthError = (message: string) => {
1313
return error;
1414
}
1515

16-
const validateJWT = async (req: Request) => {
16+
const getJWT = async (req: Request) => {
1717
const authorization = req.headers.get("Authorization")
18+
1819
if (authorization == null) {
1920
throw AuthError("Authorization header is missing")
2021
}
22+
2123
if (!authorization.startsWith("Bearer")) {
2224
throw AuthError("Bearer token is missing")
2325
}
24-
const jwt = authorization.slice("Bearer ".length)
26+
27+
return authorization.slice("Bearer ".length)
28+
}
29+
30+
const validateJWT = async (jwt: string) => {
2531
const response = await fetch(`${liferayHost}/o/oauth2/jwks`)
2632
const jwks: JSONWebKeySet = await response.json()
2733
const jwk: JWK = jwks.keys[0]
28-
const { payload } = await jwtVerify(jwt, jwk)
34+
const {payload} = await jwtVerify(jwt, jwk)
2935
log.info(`Decoded JWT: ${JSON.stringify(payload)}`)
3036
return payload
3137
}
38+
3239
const validateClientId = async (req: Request, decodedToken: JWTPayload) => {
3340
const response = await fetch(`${liferayHost}/o/oauth2/application?externalReferenceCode=${externalReferenceCode}`)
3441
const jsonResponse = await response.json()
3542
const clientId = jsonResponse["client_id"]
36-
if(clientId !== decodedToken["client_id"]) {
43+
if (clientId !== decodedToken["client_id"]) {
3744
throw AuthError("Client id from token and OAuth application don't match")
3845
}
3946
}
4047

4148
const objectAction1 = async (req: Request) => {
42-
const decodedToken = await validateJWT(req)
49+
const jwt = await getJWT(req)
50+
const decodedToken = await validateJWT(jwt)
4351
await validateClientId(req, decodedToken)
4452

45-
const objectEntry = await req.json()
46-
log.info(JSON.stringify(objectEntry))
53+
const data = await req.json()
54+
log.info(JSON.stringify(data))
55+
56+
const authorUserId = data.objectEntry.userId
57+
58+
const authorUserInfoURL = `${liferayHost}/o/headless-admin-user/v1.0/user-accounts/${authorUserId}`
59+
const headers = new Headers();
60+
headers.set('Authorization', `Bearer ${jwt}`);
61+
headers.set('Content-Type', 'application/json');
62+
63+
const requestOptions = {
64+
method: 'GET',
65+
headers: headers
66+
};
67+
68+
log.info(`Fetching author user information at ${authorUserInfoURL}`)
69+
const userInfoResponse = await fetch(`${authorUserInfoURL}`, requestOptions)
70+
71+
if (userInfoResponse.status / 100 != 2) {
72+
const errorMessage = `Could not fetch author user information: ${userInfoResponse.status} error`
73+
log.error(errorMessage)
74+
return new Response('', {
75+
status: 500,
76+
statusText: errorMessage
77+
})
78+
}
79+
80+
const authorUserInfo = await userInfoResponse.json()
81+
82+
log.info(JSON.stringify(authorUserInfo));
4783

4884
return new Response('', {
4985
status: 202

sample-etc-golang/client-extension.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ sample-etc-golang-oauth-application-user-agent:
33
.serviceScheme: http
44
name: Sample Etc Golang OAuth Application User Agent
55
scopes:
6-
- Liferay.Headless.Admin.User.read
6+
- Liferay.Headless.Admin.User.everything.read
77
type: oAuthApplicationUserAgent
88
sample-etc-golang-object-action-1:
99
name: Sample Etc Golang Object Action 1

sample-etc-golang/main.go

Lines changed: 108 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -222,10 +222,33 @@ func validateClientId(token jwt.Token) error {
222222
return errors.New("client id from token and oauth application don't match")
223223
}
224224

225+
func getJWT(r *http.Request) (string, error) {
226+
authHeader := r.Header.Get("Authorization")
227+
228+
if authHeader == "" {
229+
errorMessage := "authorization header is missing"
230+
slog.Error(errorMessage)
231+
return "", errors.New(errorMessage)
232+
}
233+
234+
tokenString := strings.Split(authHeader, "Bearer ")[1]
235+
236+
if tokenString == "" {
237+
errorMessage := "bearer token is missing"
238+
slog.Error(errorMessage)
239+
return "", errors.New(errorMessage)
240+
}
241+
return tokenString, nil
242+
}
243+
225244
func jwtHandler(next http.Handler) http.Handler {
226245
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
227-
authHeader := r.Header.Get("Authorization")
228-
tokenString := strings.Split(authHeader, "Bearer ")[1]
246+
tokenString, error := getJWT(r)
247+
248+
if error != nil {
249+
http.Error(w, error.Error(), 401)
250+
return
251+
}
229252

230253
token, err := validateJWT(tokenString)
231254

@@ -243,6 +266,70 @@ func jwtHandler(next http.Handler) http.Handler {
243266
})
244267
}
245268

269+
func getBodyByteArray(body io.ReadCloser) ([]byte, error) {
270+
var data interface{}
271+
err := json.NewDecoder(body).Decode(&data)
272+
273+
if err != nil {
274+
return nil, err
275+
}
276+
277+
dataByteArray, err := json.MarshalIndent(data, "", "\t")
278+
279+
if err != nil {
280+
return nil, err
281+
}
282+
283+
return dataByteArray, nil
284+
}
285+
286+
func fetchAuthorUserInfo(token, userId string) (string, error) {
287+
var authorUserInfoURLBuilder strings.Builder
288+
289+
protocol := Config["com.liferay.lxc.dxp.server.protocol"]
290+
host := Config["com.liferay.lxc.dxp.mainDomain"]
291+
292+
authorUserInfoURLBuilder.WriteString(protocol)
293+
authorUserInfoURLBuilder.WriteString("://")
294+
authorUserInfoURLBuilder.WriteString(host)
295+
authorUserInfoURLBuilder.WriteString("/o/headless-admin-user/v1.0/user-accounts/")
296+
authorUserInfoURLBuilder.WriteString(userId)
297+
298+
httpClient := &http.Client{}
299+
request, err := http.NewRequest("GET", authorUserInfoURLBuilder.String(), nil)
300+
301+
if err != nil {
302+
log.Fatal(err)
303+
}
304+
305+
auth := strings.Join([]string{"Bearer", token}, " ")
306+
307+
request.Header.Add("Authorization", auth)
308+
request.Header.Add("Content-Type", "application/json")
309+
310+
slog.Info(fmt.Sprintf("Fetching author user information from %s", authorUserInfoURLBuilder.String()))
311+
312+
response, err := httpClient.Do(request)
313+
314+
if err != nil {
315+
log.Fatal(err)
316+
}
317+
318+
defer response.Body.Close()
319+
320+
if response.StatusCode/100 != 2 {
321+
return "", errors.New(fmt.Sprintf("could not fetch author user information: %v error", response.StatusCode))
322+
}
323+
324+
dataByteArray, err := getBodyByteArray(response.Body)
325+
326+
if err != nil {
327+
log.Fatal(err)
328+
}
329+
330+
return string(dataByteArray), nil
331+
}
332+
246333
func main() {
247334
err := initConfig()
248335

@@ -275,21 +362,34 @@ func main() {
275362
objectAction1Handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
276363
slog.Info("execute /object/action/1")
277364

278-
var objectEntry interface{}
279-
err := json.NewDecoder(r.Body).Decode(&objectEntry)
365+
token, _ := getJWT(r)
366+
367+
dataByteArray, err := getBodyByteArray(r.Body)
368+
369+
slog.Info(string(dataByteArray))
280370

281371
if err != nil {
282372
log.Fatal(err)
283373
}
284374

285-
objectEntryJsonBytes, err := json.MarshalIndent(objectEntry, "", "\t")
375+
data := make(map[string]any)
376+
377+
err = json.Unmarshal(dataByteArray, &data)
286378

287379
if err != nil {
288-
log.Fatal(err)
380+
slog.Error(err.Error())
381+
}
382+
383+
objectEntry := data["objectEntry"].(map[string]any)
384+
authorUserId := objectEntry["userId"]
385+
386+
authorUserInfo, err := fetchAuthorUserInfo(token, fmt.Sprintf("%#v", authorUserId))
387+
388+
if err != nil {
389+
slog.Error(err.Error())
289390
}
290391

291-
objectEntryJsonString := string(objectEntryJsonBytes)
292-
slog.Info(objectEntryJsonString)
392+
slog.Info(authorUserInfo)
293393
})
294394

295395
http.Handle("/", homeHandler)

sample-etc-java-quarkus/client-extension.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ sample-etc-java-quarkus-oauth-application-user-agent:
33
.serviceScheme: http
44
name: Sample Etc Java Quarkus OAuth Application User Agent
55
scopes:
6-
- Liferay.Headless.Admin.User.read
6+
- Liferay.Headless.Admin.User.everything.read
77
type: oAuthApplicationUserAgent
88
sample-etc-java-quarkus-object-action-1:
99
name: Sample Etc Java Quarkus Object Action 1
Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,58 @@
11
package org.acme.liferay.client.extension.quarkus.sample;
22

3+
import jakarta.json.Json;
4+
import jakarta.json.JsonObject;
35
import jakarta.ws.rs.POST;
46
import jakarta.ws.rs.Path;
7+
import jakarta.ws.rs.client.Client;
8+
import jakarta.ws.rs.client.ClientBuilder;
9+
import jakarta.ws.rs.core.MediaType;
510
import jakarta.ws.rs.core.Response;
11+
import jakarta.ws.rs.core.Response.Status;
12+
import java.net.URI;
13+
import org.eclipse.microprofile.config.inject.ConfigProperty;
14+
import org.jboss.resteasy.reactive.RestHeader;
615
import org.slf4j.Logger;
716
import org.slf4j.LoggerFactory;
817

918
@Path("/object/action/1")
1019
public class ObjectAction1 {
1120

1221
@POST
13-
public Response objectAction1(String objectEntryString) {
14-
if(_log.isInfoEnabled()) {
15-
_log.info("Object Entry: {}", objectEntryString);
22+
public Response objectAction1(@RestHeader("Authorization") String authorization,
23+
JsonObject objectEntry) {
24+
25+
URI authorUserInfoURI = URI.create(
26+
liferayProtocol + "://" + liferayDomain
27+
+ "/o/headless-admin-user/v1.0/user-accounts/"
28+
+ objectEntry.getJsonObject("objectEntry").getInt("userId")
29+
);
30+
31+
if (_log.isInfoEnabled()) {
32+
_log.info("Object Entry: {}", objectEntry.toString());
33+
_log.info("Fetching author user info from {}", authorUserInfoURI);
34+
}
35+
36+
try (Client client = ClientBuilder.newClient()) {
37+
JsonObject response = client.target(authorUserInfoURI)
38+
.request(MediaType.APPLICATION_JSON_TYPE)
39+
.header("Authorization", authorization)
40+
.get(JsonObject.class);
41+
42+
if (_log.isInfoEnabled()) {
43+
_log.info(response.toString());
44+
}
1645
}
46+
1747
return Response.accepted().build();
1848
}
1949

50+
@ConfigProperty(name = "com.liferay.lxc.dxp.mainDomain")
51+
String liferayDomain;
52+
53+
@ConfigProperty(name = "com.liferay.lxc.dxp.server.protocol")
54+
String liferayProtocol;
55+
2056
private static final Logger _log = LoggerFactory.getLogger(ObjectAction1.class);
2157

2258
}

sample-etc-java-vertx/client-extension.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ sample-etc-java-vertx-oauth-application-user-agent:
33
.serviceScheme: http
44
name: Sample Etc Java Vert.x OAuth Application User Agent
55
scopes:
6-
- Liferay.Headless.Admin.User.read
6+
- Liferay.Headless.Admin.User.everything.read
77
type: oAuthApplicationUserAgent
88
sample-etc-java-vertx-object-action-1:
99
name: Sample Etc Java Vert.x Object Action 1

sample-etc-java-vertx/src/main/java/org/acme/liferay/client/extension/vertx/sample/MainVerticle.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public void start(Promise<Void> startPromise) throws Exception {
3333
_configMap = getConfigMap();
3434

3535
JWTHandler jwtHandler = new JWTHandler(vertx, webClient, _configMap);
36-
ObjectAction1Handler objectAction1Handler = new ObjectAction1Handler();
36+
ObjectAction1Handler objectAction1Handler = new ObjectAction1Handler(vertx, webClient, _configMap);
3737

3838
router.route().handler(BodyHandler.create());
3939

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package org.acme.liferay.client.extension.vertx.sample.handler;
2+
3+
import io.vertx.core.Handler;
4+
import io.vertx.core.Vertx;
5+
import io.vertx.ext.web.RoutingContext;
6+
import io.vertx.ext.web.client.WebClient;
7+
import java.util.Map;
8+
9+
public abstract class BaseHandler implements Handler<RoutingContext> {
10+
11+
public BaseHandler(Vertx vertx, WebClient webClient, Map<String, String> configMap) {
12+
_vertx = vertx;
13+
_webClient = webClient;
14+
_configMap = configMap;
15+
}
16+
17+
protected final Map<String, String> _configMap;
18+
protected final WebClient _webClient;
19+
protected final Vertx _vertx;
20+
}

sample-etc-java-vertx/src/main/java/org/acme/liferay/client/extension/vertx/sample/handler/JWTHandler.java

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package org.acme.liferay.client.extension.vertx.sample.handler;
22

3-
import io.vertx.core.Handler;
43
import io.vertx.core.Vertx;
54
import io.vertx.core.impl.logging.Logger;
65
import io.vertx.core.impl.logging.LoggerFactory;
@@ -12,12 +11,10 @@
1211
import java.util.List;
1312
import java.util.Map;
1413

15-
public class JWTHandler implements Handler<RoutingContext> {
14+
public class JWTHandler extends BaseHandler {
1615

1716
public JWTHandler(Vertx vertx, WebClient webClient, Map<String, String> configMap) {
18-
this._webClient = webClient;
19-
this._configMap = configMap;
20-
this._vertx = vertx;
17+
super(vertx, webClient, configMap);
2118
}
2219

2320
@Override
@@ -107,9 +104,5 @@ private void validateClientId(RoutingContext ctx, String tokenClientId) {
107104
});
108105
}
109106

110-
private final Map<String, String> _configMap;
111-
private final WebClient _webClient;
112-
private final Vertx _vertx;
113-
114107
private static final Logger _log = LoggerFactory.getLogger(JWTHandler.class);
115108
}

0 commit comments

Comments
 (0)