Skip to content

Commit

Permalink
Merge pull request #34 from CaritasDeutschland/feature-jwt-info
Browse files Browse the repository at this point in the history
Feature jwt info
  • Loading branch information
hill-daniel authored Apr 5, 2022
2 parents 1275a72 + cb2eca3 commit dd9172d
Show file tree
Hide file tree
Showing 19 changed files with 337 additions and 67 deletions.
45 changes: 45 additions & 0 deletions api/videoservice.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,41 @@ paths:
description: INTERNAL SERVER ERROR - server encountered unexpected condition
security:
- Bearer: [ ]
/videocalls/{roomId}/jwt:
get:
tags:
- video-controller
summary: 'Gets the moderatorToken (for consultants) or the invitationToken (for others)
[Authorization: Role: consultant, anonymous]'
operationId: getWebToken
parameters:
- name: RCUserId
in: header
required: true
schema:
type: string
- name: roomId
in: path
required: true
schema:
type: string
responses:
200:
description: OK - web token returned
content:
'application/json':
schema:
$ref: '#/components/schemas/VideoCallInfoDTO'
400:
description: BAD REQUEST - invalid/incomplete request or body object
401:
description: UNAUTHORIZED - no/invalid Keycloak token
403:
description: FORBIDDEN - no/invalid CSRF token
404:
description: NOT FOUND - rcGroupId unknown
500:
description: INTERNAL SERVER ERROR - server encountered unexpected condition

components:
schemas:
Expand Down Expand Up @@ -105,6 +140,16 @@ components:
initiatorUsername:
type: string
example: "consultant23"
VideoCallInfoDTO:
type: object
required:
- domain
- jwt
properties:
domain:
type: string
jwt:
type: string

securitySchemes:
Bearer:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@
*/
public enum Authority {

CONSULTANT("consultant", "AUTHORIZATION_CONSULTANT_DEFAULT"),
USER("user", "AUTHORIZATION_USER_DEFAULT");
CONSULTANT(UserRole.CONSULTANT, "AUTHORIZATION_CONSULTANT_DEFAULT"),
USER(UserRole.USER, "AUTHORIZATION_USER_DEFAULT");

private final String roleName;
private final UserRole role;
private final String authorityName;

Authority(final String roleName, final String authorityName) {
this.roleName = roleName;
Authority(final UserRole role, final String authorityName) {
this.role = role;
this.authorityName = authorityName;
}

Expand All @@ -26,14 +26,14 @@ public enum Authority {
*/
public static Authority fromRoleName(String roleName) {
return Stream.of(values())
.filter(authority -> authority.roleName.equals(roleName))
.filter(authority -> authority.role.getValue().equals(roleName))
.findFirst()
.orElse(null);
}

/**
* Returns the authority name for the given {@link Authority}.
*
*
* @return authority name for the given {@link Authority}
**/
public String getAuthority() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package de.caritas.cob.videoservice.api.authorization;

import lombok.AllArgsConstructor;
import lombok.Getter;

@AllArgsConstructor
@Getter
public enum UserRole {

USER("user"),
CONSULTANT("consultant");

private final String value;

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package de.caritas.cob.videoservice.api.authorization;

import static java.util.Objects.nonNull;

import com.fasterxml.jackson.annotation.JsonIgnore;
import java.util.Set;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
Expand All @@ -13,7 +17,7 @@
@NoArgsConstructor
@Getter
@Setter
public class AuthenticatedUser {
public class VideoUser {

@NonNull
private String userId;
Expand All @@ -23,4 +27,11 @@ public class AuthenticatedUser {

@NonNull
private String accessToken;

private Set<String> roles;

@JsonIgnore
public boolean isConsultant() {
return nonNull(roles) && roles.contains(UserRole.CONSULTANT.getValue());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
import de.caritas.cob.videoservice.api.model.CreateVideoCallDTO;
import de.caritas.cob.videoservice.api.model.CreateVideoCallResponseDTO;
import de.caritas.cob.videoservice.api.model.RejectVideoCallDTO;
import de.caritas.cob.videoservice.api.model.VideoCallInfoDTO;
import de.caritas.cob.videoservice.api.service.RejectVideoCallService;
import de.caritas.cob.videoservice.api.service.video.VideoCallUrlGeneratorService;
import de.caritas.cob.videoservice.generated.api.controller.VideocallsApi;
import io.swagger.annotations.Api;
import javax.validation.Valid;
Expand All @@ -25,6 +27,7 @@ public class VideoController implements VideocallsApi {

private final @NonNull StartVideoCallFacade startVideoCallFacade;
private final @NonNull RejectVideoCallService rejectVideoCallService;
private final @NonNull VideoCallUrlGeneratorService videoCallUrlGeneratorService;

/**
* Starts a new video call.
Expand All @@ -51,4 +54,11 @@ public ResponseEntity<Void> rejectVideoCall(@Valid RejectVideoCallDTO rejectVide
this.rejectVideoCallService.rejectVideoCall(rejectVideoCallDto);
return new ResponseEntity<>(HttpStatus.OK);
}

@Override
public ResponseEntity<VideoCallInfoDTO> getWebToken(String rcUserId, String roomId) {
var videoCallInfo = videoCallUrlGeneratorService.generateJwt(roomId);

return ResponseEntity.ok(videoCallInfo);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import static de.caritas.cob.videoservice.api.service.session.SessionStatus.IN_PROGRESS;
import static java.util.Collections.singletonList;

import de.caritas.cob.videoservice.api.authorization.AuthenticatedUser;
import de.caritas.cob.videoservice.api.authorization.VideoUser;
import de.caritas.cob.videoservice.api.exception.httpresponse.BadRequestException;
import de.caritas.cob.videoservice.api.model.CreateVideoCallResponseDTO;
import de.caritas.cob.videoservice.api.service.LogService;
Expand Down Expand Up @@ -31,7 +31,7 @@ public class StartVideoCallFacade {

private final @NonNull SessionService sessionService;
private final @NonNull LiveEventNotificationService liveEventNotificationService;
private final @NonNull AuthenticatedUser authenticatedUser;
private final @NonNull VideoUser authenticatedUser;
private final @NonNull VideoCallUrlGeneratorService videoCallUrlGeneratorService;
private final @NonNull UuidRegistry uuidRegistry;
private final @NonNull StatisticsService statisticsService;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package de.caritas.cob.videoservice.api.service.securityheader;

import de.caritas.cob.videoservice.api.authorization.AuthenticatedUser;
import de.caritas.cob.videoservice.api.authorization.VideoUser;
import java.util.UUID;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
Expand All @@ -13,7 +13,7 @@
@RequiredArgsConstructor
public class SecurityHeaderSupplier {

private final @NonNull AuthenticatedUser authenticatedUser;
private final @NonNull VideoUser authenticatedUser;

@Value("${csrf.header.property}")
private String csrfHeaderProperty;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package de.caritas.cob.videoservice.api.service.video;

import de.caritas.cob.videoservice.api.exception.httpresponse.InternalServerErrorException;
import de.caritas.cob.videoservice.api.model.VideoCallInfoDTO;
import de.caritas.cob.videoservice.api.service.video.jwt.TokenGeneratorService;
import de.caritas.cob.videoservice.api.service.video.jwt.model.VideoCallUrls;
import java.net.MalformedURLException;
Expand Down Expand Up @@ -57,4 +58,17 @@ private String buildUrl(String uuid, String token) {
}
}

/**
* Generate JWT.
*
* @param roomId room id
* @return VideoCallInfoDTO
*/
public VideoCallInfoDTO generateJwt(String roomId) {
var videoCallInfo = new VideoCallInfoDTO();
videoCallInfo.setJwt(tokenGeneratorService.generateToken(roomId));
videoCallInfo.setDomain(videoCallServerUrl);

return videoCallInfo;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator.Builder;
import com.auth0.jwt.algorithms.Algorithm;
import de.caritas.cob.videoservice.api.authorization.AuthenticatedUser;
import de.caritas.cob.videoservice.api.authorization.VideoUser;
import de.caritas.cob.videoservice.api.exception.httpresponse.InternalServerErrorException;
import de.caritas.cob.videoservice.api.service.decoder.UsernameDecoder;
import de.caritas.cob.videoservice.api.service.video.jwt.model.VideoCallToken;
Expand All @@ -16,7 +16,8 @@
import java.util.HashMap;
import java.util.Map;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;
Expand All @@ -25,11 +26,16 @@
/**
* JWT token generator service.
*/
@RequiredArgsConstructor
@Service
public class TokenGeneratorService {

private final @NonNull AuthenticatedUser authenticatedUser;
@Autowired
public TokenGeneratorService(
@NonNull @Qualifier("AuthenticatedOrAnonymousUser") VideoUser authenticatedUser) {
this.videoUser = authenticatedUser;
}

private final @NonNull VideoUser videoUser;

private static final String CONTEXT_CLAIM = "context";
private static final String ROOM_CLAIM = "room";
Expand Down Expand Up @@ -60,6 +66,18 @@ public void initAlgorithm() {
this.algorithm = Algorithm.HMAC256(this.secret);
}

/**
* Generate token.
*
* @param roomId room id
* @return token
*/
public String generateToken(String roomId) {
return videoUser.isConsultant()
? generateModeratorToken(roomId)
: generateNonModeratorToken(roomId);
}

/**
* Generates the {@link VideoCallToken} for anonymous user and asker (containing user name).
*
Expand All @@ -69,12 +87,12 @@ public void initAlgorithm() {
*/
public VideoCallToken generateNonModeratorToken(String roomId, String askerName) {
return VideoCallToken.builder()
.guestToken(buildGuestJwt(roomId))
.guestToken(generateNonModeratorToken(roomId))
.userRelatedToken(buildUserRelatedJwt(roomId, askerName))
.build();
}

private String buildGuestJwt(String roomId) {
public String generateNonModeratorToken(String roomId) {
return buildBasicJwt(roomId)
.sign(algorithm);
}
Expand Down Expand Up @@ -128,10 +146,25 @@ public String generateModeratorToken(String roomId, String guestVideoCallUrl) {
return buildModeratorJwt(roomId, guestVideoCallUrl);
}

/**
* Generate moderator token.
*
* @param roomId room id
* @return token
*/
public String generateModeratorToken(String roomId) {
var userContext = createUserContext(videoUser.getUsername());

return buildBasicJwt(roomId)
.withClaim(MODERATOR_CLAIM, true)
.withClaim(CONTEXT_CLAIM, userContext)
.sign(algorithm);
}

private String buildModeratorJwt(String roomId, String guestVideoCallUrl) {
return buildBasicJwt(roomId)
.withClaim(MODERATOR_CLAIM, true)
.withClaim(CONTEXT_CLAIM, createUserContext(authenticatedUser.getUsername()))
.withClaim(CONTEXT_CLAIM, createUserContext(videoUser.getUsername()))
.withClaim(GUEST_URL_CLAIM, guestVideoCallUrl)
.sign(algorithm);
}
Expand Down
Loading

0 comments on commit dd9172d

Please sign in to comment.