Skip to content

Commit

Permalink
feat: enable web token creation for anonymous users
Browse files Browse the repository at this point in the history
  • Loading branch information
hill-daniel committed Apr 5, 2022
1 parent 359048b commit cb2eca3
Show file tree
Hide file tree
Showing 15 changed files with 223 additions and 69 deletions.
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
Expand Up @@ -17,7 +17,7 @@
@NoArgsConstructor
@Getter
@Setter
public class AuthenticatedUser {
public class VideoUser {

@NonNull
private String userId;
Expand All @@ -32,6 +32,6 @@ public class AuthenticatedUser {

@JsonIgnore
public boolean isConsultant() {
return nonNull(roles) && roles.contains(Authority.CONSULTANT.name());
return nonNull(roles) && roles.contains(UserRole.CONSULTANT.getValue());
}
}
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
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 @@ -67,7 +73,7 @@ public void initAlgorithm() {
* @return token
*/
public String generateToken(String roomId) {
return authenticatedUser.isConsultant()
return videoUser.isConsultant()
? generateModeratorToken(roomId)
: generateNonModeratorToken(roomId);
}
Expand Down Expand Up @@ -147,7 +153,7 @@ public String generateModeratorToken(String roomId, String guestVideoCallUrl) {
* @return token
*/
public String generateModeratorToken(String roomId) {
var userContext = createUserContext(authenticatedUser.getUsername());
var userContext = createUserContext(videoUser.getUsername());

return buildBasicJwt(roomId)
.withClaim(MODERATOR_CLAIM, true)
Expand All @@ -158,7 +164,7 @@ public String generateModeratorToken(String roomId) {
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
Original file line number Diff line number Diff line change
Expand Up @@ -2,43 +2,68 @@

import static java.util.Objects.isNull;

import de.caritas.cob.videoservice.api.authorization.AuthenticatedUser;
import de.caritas.cob.videoservice.api.authorization.VideoUser;
import de.caritas.cob.videoservice.api.exception.KeycloakException;
import java.security.Principal;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.keycloak.KeycloakSecurityContext;
import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

/**
* Configuration for the {@link AuthenticatedUser}.
* Configuration for the {@link VideoUser}.
*/
@Configuration
public class AuthenticatedUserConfig {

private static final String CLAIM_NAME_USER_ID = "userId";
private static final String CLAIM_NAME_USERNAME = "username";
private static final VideoUser ANONYMOUS_USER = new VideoUser();

/**
* Returns the currently authenticated user.
*
* @return {@link AuthenticatedUser}
* @return {@link VideoUser}
*/
@Bean
@Primary
@Scope(scopeName = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public AuthenticatedUser getAuthenticatedUser() {
public VideoUser getAuthenticatedUser() {
var userPrincipal = getRequest().getUserPrincipal();
return createAuthenticatedUser(userPrincipal);
}

var keycloakSecContext = ((KeycloakAuthenticationToken) getRequest().getUserPrincipal())
.getAccount().getKeycloakSecurityContext();
/**
* Returns the currently authenticated user, or an anonymous representation.
*
* @return {@link VideoUser}
*/
@Bean
@Qualifier("AuthenticatedOrAnonymousUser")
@Scope(scopeName = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public VideoUser getAuthenticatedOrAnonymousUser() {
var userPrincipal = getRequest().getUserPrincipal();
if (isNull(userPrincipal)) {
return ANONYMOUS_USER;
}
return createAuthenticatedUser(userPrincipal);
}

private VideoUser createAuthenticatedUser(Principal userPrincipal) {
var keycloakUser = (KeycloakAuthenticationToken) userPrincipal;
var keycloakSecContext = keycloakUser.getAccount().getKeycloakSecurityContext();
Map<String, Object> claimMap = keycloakSecContext.getToken().getOtherClaims();

var authenticatedUser = new AuthenticatedUser();
var authenticatedUser = new VideoUser();
authenticatedUser.setAccessToken(getUserAccessToken(keycloakSecContext));
authenticatedUser.setUserId(getUserAttribute(claimMap, CLAIM_NAME_USER_ID));
authenticatedUser.setUsername(getUserAttribute(claimMap, CLAIM_NAME_USERNAME));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ protected void configure(HttpSecurity http) throws Exception {
.antMatchers("/videocalls/reject")
.hasAnyAuthority(USER.getAuthority())
.antMatchers("/videocalls/*/jwt")
.hasAnyAuthority(CONSULTANT.getAuthority()) //TODO: add anonymous
.permitAll()
.anyRequest().denyAll()
.and()
.exceptionHandling()
Expand Down
7 changes: 7 additions & 0 deletions src/main/resources/application-testing.properties
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,10 @@ csrf.cookie.property=csrfCookie

# Statistics
statistics.enabled=true

# JWT
video.call.security.jwt.audience=test_server
video.call.security.jwt.issuer=test_app_client
video.call.security.jwt.subject=meet.jitsi
video.call.security.jwt.secret=95148E36-19AA-4A4B-8F5A-FC6245A36912
video.call.security.jwt.validity.hours=3
Loading

0 comments on commit cb2eca3

Please sign in to comment.