package org.gcube.keycloak.avatar; import java.io.InputStream; import javax.ws.rs.Consumes; import javax.ws.rs.ForbiddenException; import javax.ws.rs.GET; import javax.ws.rs.NotAuthorizedException; import javax.ws.rs.NotFoundException; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.Context; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.Response; import org.gcube.keycloak.avatar.storage.AvatarStorageProvider; import org.jboss.resteasy.annotations.cache.NoCache; import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataInput; import org.keycloak.common.ClientConnection; import org.keycloak.jose.jws.JWSInput; import org.keycloak.jose.jws.JWSInputException; import org.keycloak.models.ClientModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; import org.keycloak.models.UserModel; import org.keycloak.representations.AccessToken; import org.keycloak.services.managers.AppAuthManager; import org.keycloak.services.managers.AuthenticationManager; import org.keycloak.services.managers.RealmManager; import org.keycloak.services.resources.admin.AdminAuth; import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator; import org.keycloak.services.resources.admin.permissions.AdminPermissions; public class AvatarAdminResource extends AbstractAvatarResource { private AdminPermissionEvaluator realmAuth; @Context private AvatarStorageProvider avatarStorageProvider; private AppAuthManager authManager; // private TokenManager tokenManager; @Context private HttpHeaders httpHeaders; @Context private ClientConnection clientConnection; private AdminAuth auth; public AvatarAdminResource(KeycloakSession session) { super(session); authManager = new AppAuthManager(); // tokenManager = new TokenManager(); } public void init() { RealmModel realm = session.getContext().getRealm(); auth = authenticateRealmAdminRequest(); RealmManager realmManager = new RealmManager(session); if (realm == null) throw new NotFoundException("Realm not found"); if (!auth.getRealm().equals(realmManager.getKeycloakAdminstrationRealm()) && !auth.getRealm().equals(realm)) { throw new org.keycloak.services.ForbiddenException(); } realmAuth = AdminPermissions.evaluator(session, realm, auth); session.getContext().setRealm(realm); } @GET @Path("/{user_id}") @Produces({ "image/png", "image/jpeg", "image/gif" }) public Response downloadUserAvatarImage(@PathParam("user_id") String userId) { try { canViewUsers(); UserModel user = session.users().getUserById(userId, session.getContext().getRealm()); return Response.ok(fetchUserImage(session.getContext().getRealm(), user)).build(); } catch (ForbiddenException e) { return Response.status(Response.Status.FORBIDDEN).entity(e.getMessage()).build(); } catch (Exception e) { logger.error("error getting user avatar", e); return Response.serverError().entity(e.getMessage()).build(); } } @POST @NoCache @Path("/{user_id}") @Consumes(MediaType.MULTIPART_FORM_DATA) public Response uploadUserAvatarImage(@PathParam("user_id") String userId, MultipartFormDataInput input) { try { if (auth == null) { return Response.status(Response.Status.UNAUTHORIZED).build(); } canManageUsers(); RealmModel realm = session.getContext().getRealm(); UserModel user = session.users().getUserById(userId, session.getContext().getRealm()); InputStream imageInputStream = input.getFormDataPart(AVATAR_IMAGE_PARAMETER, InputStream.class, null); saveUserImage(realm, user, imageInputStream); } catch (ForbiddenException e) { return Response.status(Response.Status.FORBIDDEN).entity(e.getMessage()).build(); } catch (Exception e) { logger.error("error saving user avatar", e); return Response.serverError().entity(e.getMessage()).build(); } return Response.ok().build(); } protected AdminAuth authenticateRealmAdminRequest() { String tokenString = authManager.extractAuthorizationHeaderToken(httpHeaders); MultivaluedMap queryParameters = session.getContext().getUri().getQueryParameters(); if (tokenString == null && queryParameters.containsKey("access_token")) { tokenString = queryParameters.getFirst("access_token"); } if (tokenString == null) { throw new NotAuthorizedException("Bearer"); } AccessToken token; try { JWSInput input = new JWSInput(tokenString); token = input.readJsonContent(AccessToken.class); } catch (JWSInputException e) { throw new NotAuthorizedException("Bearer token format error"); } String realmName = token.getIssuer().substring(token.getIssuer().lastIndexOf('/') + 1); RealmManager realmManager = new RealmManager(session); RealmModel realm = realmManager.getRealmByName(realmName); if (realm == null) { throw new NotAuthorizedException("Unknown realm in token"); } session.getContext().setRealm(realm); AuthenticationManager.AuthResult authResult = authManager.authenticateBearerToken(tokenString, session, realm, session.getContext().getUri(), clientConnection, httpHeaders); if (authResult == null) { logger.debug("Token not valid"); throw new NotAuthorizedException("Bearer"); } ClientModel client = realm.getClientByClientId(token.getIssuedFor()); if (client == null) { throw new NotFoundException("Could not find client for authorization"); } return new AdminAuth(realm, authResult.getToken(), authResult.getUser(), client); } private void canViewUsers() { if (!realmAuth.users().canView()) { String message = "user does not have permission to view users"; logger.info(message); throw new ForbiddenException(message); } } private void canManageUsers() { if (!realmAuth.users().canManage()) { String message = "user does not have permission to manage users"; logger.info(message); throw new ForbiddenException(message); } } }