From 495e1d948227a0a3b4adbaedba38f997418d638e Mon Sep 17 00:00:00 2001 From: Felix Sidokhine <felix.sidokhine@savoirfairelinux.com> Date: Wed, 1 Jul 2020 15:42:59 +0300 Subject: [PATCH] cleaned-up authentication and reduced code bloat Change-Id: Id41e329bc44a9bdf1266ca0402b655186fc02856 --- .../jami/jams/authmodule/TokenController.java | 3 +- .../net/jami/datastore/main/DataStore.java | 7 +- .../jams/common/objects/user/UserProfile.java | 2 - .../api/admin/devices/DeviceServlet.java | 5 + .../api/admin/devices/DevicesServlet.java | 3 + .../directory/DirectoryEntryServlet.java | 76 +++++++-------- .../api/admin/update/SubscriptionServlet.java | 4 + .../api/admin/update/UpdateServlet.java | 4 + .../servlets/api/admin/users/UserServlet.java | 6 ++ .../api/admin/users/UsersServlet.java | 3 + .../api/auth/device/DeviceServlet.java | 4 +- .../directory/SearchDirectoryServlet.java | 1 - .../servlets/api/auth/user/UserServlet.java | 4 - .../api/install/CreateAuthSourceServlet.java | 4 + .../servlets/api/install/CreateCAServlet.java | 4 + .../install/CreateServerSettingsServlet.java | 4 + .../api/install/InstallLastStepServlet.java | 3 + .../api/install/StartInstallServlet.java | 5 +- .../servlets/filters/AdminApiFilter.java | 86 ----------------- .../server/servlets/filters/ApiFilter.java | 53 ++-------- .../servlets/filters/AuthRequestType.java | 8 ++ .../server/servlets/filters/FilterUtils.java | 96 +++++++++++++++++++ .../servlets/filters/InstallFilter.java | 21 +--- .../server/servlets/filters/JWTValidator.java | 50 ---------- .../src/main/resources/webapp/js/api.js | 2 +- .../src/main/resources/webapp/js/user.js | 6 +- ldap-connector/pom.xml | 17 ++++ .../src/test/java/tests/GenericLDAPTest.java | 61 ++++++++++++ .../src/test/resources/bootstrap.ldif | 45 +++++++++ .../src/test/resources/ldapconfig.json | 20 ++++ 30 files changed, 350 insertions(+), 257 deletions(-) delete mode 100644 jams-server/src/main/java/net/jami/jams/server/servlets/filters/AdminApiFilter.java create mode 100644 jams-server/src/main/java/net/jami/jams/server/servlets/filters/AuthRequestType.java create mode 100644 jams-server/src/main/java/net/jami/jams/server/servlets/filters/FilterUtils.java delete mode 100644 jams-server/src/main/java/net/jami/jams/server/servlets/filters/JWTValidator.java create mode 100644 ldap-connector/src/test/java/tests/GenericLDAPTest.java create mode 100644 ldap-connector/src/test/resources/bootstrap.ldif create mode 100644 ldap-connector/src/test/resources/ldapconfig.json diff --git a/authentication-module/src/main/java/net/jami/jams/authmodule/TokenController.java b/authentication-module/src/main/java/net/jami/jams/authmodule/TokenController.java index 6b2ee493..8f74a124 100644 --- a/authentication-module/src/main/java/net/jami/jams/authmodule/TokenController.java +++ b/authentication-module/src/main/java/net/jami/jams/authmodule/TokenController.java @@ -60,9 +60,11 @@ public class TokenController{ if(deviceId != null){ builder.claim("scope",DEVICE); builder.claim("deviceId",deviceId); + authTokenResponse.setScope(DEVICE); } else{ builder.claim("scope",user.getAccessLevel()); + authTokenResponse.setScope(user.getAccessLevel()); } JWTClaimsSet jwtClaims = builder.build(); SignedJWT signedJWT = new SignedJWT(jwsHeader, jwtClaims); @@ -71,7 +73,6 @@ public class TokenController{ UserAuthenticationModule.datastore.getJwtDao().storeObject(signedJWT); authTokenResponse.setAccess_token(signedJWT.serialize()); authTokenResponse.setExpires_in(30*60*1000L); - authTokenResponse.setScope(user.getAccessLevel()); authTokenResponse.setToken_type("Bearer"); return authTokenResponse; } diff --git a/datastore/src/main/java/net/jami/datastore/main/DataStore.java b/datastore/src/main/java/net/jami/datastore/main/DataStore.java index 4579b802..508711af 100644 --- a/datastore/src/main/java/net/jami/datastore/main/DataStore.java +++ b/datastore/src/main/java/net/jami/datastore/main/DataStore.java @@ -24,7 +24,12 @@ package net.jami.datastore.main; import lombok.Getter; import lombok.Setter; -import net.jami.datastore.dao.*; +import net.jami.datastore.dao.ContactDao; +import net.jami.datastore.dao.DeviceDao; +import net.jami.datastore.dao.JwtDao; +import net.jami.datastore.dao.SystemDao; +import net.jami.datastore.dao.UserDao; +import net.jami.datastore.dao.UserProfileDao; import net.jami.jams.common.authentication.AuthenticationSource; import net.jami.jams.common.authentication.AuthenticationSourceInfo; import net.jami.jams.common.authentication.AuthenticationSourceType; diff --git a/jams-common/src/main/java/net/jami/jams/common/objects/user/UserProfile.java b/jams-common/src/main/java/net/jami/jams/common/objects/user/UserProfile.java index c82f15bc..ff274084 100644 --- a/jams-common/src/main/java/net/jami/jams/common/objects/user/UserProfile.java +++ b/jams-common/src/main/java/net/jami/jams/common/objects/user/UserProfile.java @@ -35,9 +35,7 @@ import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; -import net.jami.jams.common.authentication.AuthenticationSourceType; import net.jami.jams.common.serialization.database.DatabaseObject; -import net.jami.jams.common.utils.X509Utils; import org.bouncycastle.util.encoders.Base64; import java.lang.reflect.Method; diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/devices/DeviceServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/devices/DeviceServlet.java index ef068abb..d12c92f5 100644 --- a/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/devices/DeviceServlet.java +++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/devices/DeviceServlet.java @@ -28,9 +28,11 @@ import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import net.jami.jams.common.annotations.ScopedServletMethod; import net.jami.jams.common.dao.StatementElement; import net.jami.jams.common.dao.StatementList; import net.jami.jams.common.objects.responses.DeviceRevocationResponse; +import net.jami.jams.common.objects.user.AccessLevel; import net.jami.jams.server.core.workflows.RevokeDeviceFlow; import java.io.IOException; @@ -42,6 +44,7 @@ public class DeviceServlet extends HttpServlet { //Get a detailed device info. @Override + @ScopedServletMethod(securityGroups = {AccessLevel.ADMIN}) protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String username = req.getParameter("username"); String deviceId = req.getParameter("deviceId"); @@ -55,6 +58,7 @@ public class DeviceServlet extends HttpServlet { //Update device data. @Override + @ScopedServletMethod(securityGroups = {AccessLevel.ADMIN}) protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String username = req.getParameter("username"); String deviceId = req.getParameter("deviceId"); @@ -73,6 +77,7 @@ public class DeviceServlet extends HttpServlet { //Revoke/delete a device. @Override + @ScopedServletMethod(securityGroups = {AccessLevel.ADMIN}) protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { DeviceRevocationResponse devResponse = RevokeDeviceFlow.revokeDevice(req.getParameter("username").toString(),req.getParameter("deviceId")); if(devResponse != null) resp.getOutputStream().write(JsonStream.serialize(devResponse).getBytes()); diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/devices/DevicesServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/devices/DevicesServlet.java index 05dca792..161a9e75 100644 --- a/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/devices/DevicesServlet.java +++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/devices/DevicesServlet.java @@ -28,8 +28,10 @@ import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import net.jami.jams.common.annotations.ScopedServletMethod; import net.jami.jams.common.dao.StatementElement; import net.jami.jams.common.dao.StatementList; +import net.jami.jams.common.objects.user.AccessLevel; import java.io.IOException; @@ -40,6 +42,7 @@ public class DevicesServlet extends HttpServlet { //Get a list of devices for a user. @Override + @ScopedServletMethod(securityGroups = {AccessLevel.ADMIN}) protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String username = req.getParameter("username"); StatementList statementList = new StatementList(); diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/directory/DirectoryEntryServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/directory/DirectoryEntryServlet.java index 8aa3c8e3..3ccddf07 100644 --- a/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/directory/DirectoryEntryServlet.java +++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/directory/DirectoryEntryServlet.java @@ -1,25 +1,26 @@ /* -* Copyright (C) 2020 by Savoir-faire Linux -* Authors: William Enright <william.enright@savoirfairelinux.com> -* Ndeye Anna Ndiaye <anna.ndiaye@savoirfairelinux.com> -* Johnny Flores <johnny.flores@savoirfairelinux.com> -* Mohammed Raza <mohammed.raza@savoirfairelinux.com> -* Felix Sidokhine <felix.sidokhine@savoirfairelinux.com> -* -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see <https://www.gnu.org/licenses/>. -*/ + * Copyright (C) 2020 by Savoir-faire Linux + * Authors: William Enright <william.enright@savoirfairelinux.com> + * Ndeye Anna Ndiaye <anna.ndiaye@savoirfairelinux.com> + * Johnny Flores <johnny.flores@savoirfairelinux.com> + * Mohammed Raza <mohammed.raza@savoirfairelinux.com> + * Felix Sidokhine <felix.sidokhine@savoirfairelinux.com> + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + package net.jami.jams.server.servlets.api.admin.directory; import com.jsoniter.JsonIterator; @@ -29,12 +30,12 @@ import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; -import lombok.extern.slf4j.Slf4j; -import net.jami.datastore.main.DataStore; +import net.jami.jams.common.annotations.ScopedServletMethod; import net.jami.jams.common.authentication.AuthenticationSourceType; import net.jami.jams.common.authmodule.AuthModuleKey; import net.jami.jams.common.dao.StatementElement; import net.jami.jams.common.dao.StatementList; +import net.jami.jams.common.objects.user.AccessLevel; import net.jami.jams.common.objects.user.UserProfile; import java.io.IOException; @@ -45,32 +46,24 @@ import static net.jami.jams.server.Server.dataStore; @WebServlet("/api/admin/directory/entry") -@Slf4j public class DirectoryEntryServlet extends HttpServlet { @Override + @ScopedServletMethod(securityGroups = {AccessLevel.ADMIN}) protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //Create a user profile. - try { - String realm = "LOCAL"; - UserProfile userProfile = JsonIterator.deserialize(req.getInputStream().readAllBytes(), UserProfile.class); - userAuthenticationModule.getAuthSources().get(new AuthModuleKey(realm, AuthenticationSourceType.LOCAL)) - .setUserProfile(userProfile); - - resp.setStatus(200); - HashMap<String,String> profileName = new HashMap<>(); - profileName.put("username", userProfile.getUsername()); - resp.getOutputStream().write(JsonStream.serialize(profileName).getBytes()); - } - catch (Exception e){ - log.error("Could not store a user profile with error {}",e.getMessage()); - resp.sendError(500,e.getMessage()); - } - + String realm = "LOCAL"; + UserProfile userProfile = JsonIterator.deserialize(req.getInputStream().readAllBytes(), UserProfile.class); + userAuthenticationModule.getAuthSources().get(new AuthModuleKey(realm, AuthenticationSourceType.LOCAL)) + .setUserProfile(userProfile); + resp.setStatus(200); + HashMap<String, String> profileName = new HashMap<>(); + profileName.put("username", userProfile.getUsername()); + resp.getOutputStream().write(JsonStream.serialize(profileName).getBytes()); } @Override - protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + protected void doPut(HttpServletRequest req, HttpServletResponse resp) { //Update a user's profile. try{ //Check if he is AD/LDAP - then return a 500, because we can't update those profile datas. @@ -91,6 +84,7 @@ public class DirectoryEntryServlet extends HttpServlet { } @Override + @ScopedServletMethod(securityGroups = {AccessLevel.ADMIN}) protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //This method will probably never be implemented. } diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/update/SubscriptionServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/update/SubscriptionServlet.java index e30132d3..333e55f6 100644 --- a/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/update/SubscriptionServlet.java +++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/update/SubscriptionServlet.java @@ -29,7 +29,9 @@ import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import net.jami.jams.ca.JamsCA; +import net.jami.jams.common.annotations.ScopedServletMethod; import net.jami.jams.common.objects.responses.SubscriptionStatusResponse; +import net.jami.jams.common.objects.user.AccessLevel; import net.jami.jams.server.Server; import java.io.IOException; @@ -39,6 +41,7 @@ public class SubscriptionServlet extends HttpServlet { //Get the subscription status (see: SubscriptionStatusResponse.class) @Override + @ScopedServletMethod(securityGroups = {AccessLevel.ADMIN}) protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setHeader("Access-Control-Allow-Origin", JamsCA.serverDomain); resp.setContentType("application/json"); @@ -51,6 +54,7 @@ public class SubscriptionServlet extends HttpServlet { //Upload the license here, which is really just uploading a base64 representation of the keypair - and store it // on disk.. @Override + @ScopedServletMethod(securityGroups = {AccessLevel.ADMIN}) protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.getInputStream().readAllBytes(); diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/update/UpdateServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/update/UpdateServlet.java index 1b3a5e45..b1d0aa58 100644 --- a/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/update/UpdateServlet.java +++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/update/UpdateServlet.java @@ -28,6 +28,8 @@ import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import net.jami.jams.common.annotations.ScopedServletMethod; +import net.jami.jams.common.objects.user.AccessLevel; import net.jami.jams.common.updater.FullSystemStatusResponse; import java.io.IOException; @@ -39,6 +41,7 @@ public class UpdateServlet extends HttpServlet { //Return the current version number and the available version number. @Override + @ScopedServletMethod(securityGroups = {AccessLevel.ADMIN}) protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { FullSystemStatusResponse response = new FullSystemStatusResponse(); response.setLocalVersions(appUpdater.getLocalVersions()); @@ -49,6 +52,7 @@ public class UpdateServlet extends HttpServlet { //This is the do-update button. @Override + @ScopedServletMethod(securityGroups = {AccessLevel.ADMIN}) protected void doPost(HttpServletRequest req, HttpServletResponse resp) { if(appUpdater.getUpdateAvailable()){ appUpdater.doUpdate(); diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/users/UserServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/users/UserServlet.java index 252d8814..d8ad89d7 100644 --- a/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/users/UserServlet.java +++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/users/UserServlet.java @@ -28,10 +28,12 @@ import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import net.jami.jams.common.annotations.ScopedServletMethod; import net.jami.jams.common.authentication.AuthenticationSourceType; import net.jami.jams.common.dao.StatementElement; import net.jami.jams.common.dao.StatementList; import net.jami.jams.common.objects.responses.DeviceRevocationResponse; +import net.jami.jams.common.objects.user.AccessLevel; import net.jami.jams.common.objects.user.User; import net.jami.jams.common.utils.PasswordGenerator; import net.jami.jams.server.core.workflows.RevokeUserFlow; @@ -48,6 +50,7 @@ public class UserServlet extends HttpServlet { //Get the user profile. @Override + @ScopedServletMethod(securityGroups = {AccessLevel.ADMIN}) protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { StatementList statementList = new StatementList(); StatementElement st1 = new StatementElement("username","=",req.getParameter("username"),""); @@ -57,6 +60,7 @@ public class UserServlet extends HttpServlet { //Create an internal user - this is always technically available, because internal users have the right to exist. @Override + @ScopedServletMethod(securityGroups = {AccessLevel.ADMIN}) protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { User user = new User(); user.setUsername(req.getParameter("username")); @@ -76,6 +80,7 @@ public class UserServlet extends HttpServlet { //Update user data. @Override + @ScopedServletMethod(securityGroups = {AccessLevel.ADMIN}) protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws IOException { String username = req.getParameter("username"); //Check if he is AD/LDAP - then return a 403, because we can't set such password. @@ -95,6 +100,7 @@ public class UserServlet extends HttpServlet { //Revoke a user. @Override + @ScopedServletMethod(securityGroups = {AccessLevel.ADMIN}) protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws IOException { DeviceRevocationResponse devResponse = RevokeUserFlow.revokeUser(req.getParameter("username")); if(devResponse != null && devResponse.isSuccess()) resp.getOutputStream().write(JsonStream.serialize(devResponse).getBytes()); diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/users/UsersServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/users/UsersServlet.java index 179f241d..d3dd0e4b 100644 --- a/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/users/UsersServlet.java +++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/users/UsersServlet.java @@ -28,6 +28,8 @@ import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import net.jami.jams.common.annotations.ScopedServletMethod; +import net.jami.jams.common.objects.user.AccessLevel; import java.io.IOException; @@ -38,6 +40,7 @@ public class UsersServlet extends HttpServlet { //Returns a list of users. @Override + @ScopedServletMethod(securityGroups = {AccessLevel.ADMIN}) protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.getOutputStream().write(JsonStream.serialize(dataStore.getUserDao().getObjects(null).get(0)).getBytes()); } diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/device/DeviceServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/device/DeviceServlet.java index cb7123b5..c80845ad 100644 --- a/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/device/DeviceServlet.java +++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/device/DeviceServlet.java @@ -42,7 +42,6 @@ import net.jami.jams.server.core.workflows.RegisterDeviceFlow; import net.jami.jams.server.core.workflows.RevokeDeviceFlow; import java.io.IOException; -import java.util.stream.Collectors; import static net.jami.jams.server.Server.certificateAuthority; import static net.jami.jams.server.Server.dataStore; @@ -171,8 +170,9 @@ public class DeviceServlet extends HttpServlet { StatementElement statementElement = new StatementElement("owner","=",req.getAttribute("username").toString(),""); statementList.addStatement(statementElement); if(dataStore.getDeviceDao().getObjects(statementList).stream().filter(device -> - device.getDeviceId().equals(deviceId)).collect(Collectors.toList()).size() == 0){ + device.getDeviceId().equals(deviceId)).count() == 0){ TomcatCustomErrorHandler.sendCustomError(resp,403,"You do not have sufficient rights to revoke this device!"); + return; } DeviceRevocationResponse devResponse = RevokeDeviceFlow.revokeDevice(req.getAttribute("username").toString(),deviceId); if(devResponse != null) resp.getOutputStream().write(JsonStream.serialize(devResponse).getBytes()); diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/directory/SearchDirectoryServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/directory/SearchDirectoryServlet.java index 1f940c8b..e236985f 100644 --- a/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/directory/SearchDirectoryServlet.java +++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/directory/SearchDirectoryServlet.java @@ -23,7 +23,6 @@ package net.jami.jams.server.servlets.api.auth.directory; import com.jsoniter.output.JsonStream; -import jakarta.servlet.ServletException; import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/user/UserServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/user/UserServlet.java index 752d944a..80c5aee1 100644 --- a/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/user/UserServlet.java +++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/user/UserServlet.java @@ -22,7 +22,6 @@ */ package net.jami.jams.server.servlets.api.auth.user; -import com.jsoniter.output.JsonStream; import jakarta.servlet.ServletException; import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; @@ -35,14 +34,11 @@ import net.jami.jams.common.dao.StatementList; import net.jami.jams.common.objects.user.AccessLevel; import net.jami.jams.common.objects.user.User; import net.jami.jams.common.utils.PasswordGenerator; -import org.bouncycastle.openssl.PEMWriter; import java.io.IOException; -import java.io.StringWriter; import static net.jami.jams.server.Server.certificateAuthority; import static net.jami.jams.server.Server.dataStore; -import static net.jami.jams.server.Server.nameServer; @WebServlet("/api/auth/user") public class UserServlet extends HttpServlet { diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/install/CreateAuthSourceServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/install/CreateAuthSourceServlet.java index d0637660..51a757fb 100644 --- a/jams-server/src/main/java/net/jami/jams/server/servlets/api/install/CreateAuthSourceServlet.java +++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/install/CreateAuthSourceServlet.java @@ -30,8 +30,10 @@ import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; +import net.jami.jams.common.annotations.ScopedServletMethod; import net.jami.jams.common.authentication.AuthenticationSourceType; import net.jami.jams.common.objects.requests.CreateAuthSourceRequest; +import net.jami.jams.common.objects.user.AccessLevel; import java.io.IOException; @@ -42,11 +44,13 @@ import static net.jami.jams.server.Server.userAuthenticationModule; public class CreateAuthSourceServlet extends HttpServlet { @Override + @ScopedServletMethod(securityGroups = {AccessLevel.ADMIN}) protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { super.doGet(req, resp); } @Override + @ScopedServletMethod(securityGroups = {AccessLevel.ADMIN}) protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { CreateAuthSourceRequest authSourceRequest = JsonIterator.deserialize( req.getInputStream().readAllBytes(),CreateAuthSourceRequest.class); diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/install/CreateCAServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/install/CreateCAServlet.java index b8c54864..7a813d1d 100644 --- a/jams-server/src/main/java/net/jami/jams/server/servlets/api/install/CreateCAServlet.java +++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/install/CreateCAServlet.java @@ -28,7 +28,9 @@ import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import net.jami.jams.common.annotations.ScopedServletMethod; import net.jami.jams.common.objects.requests.CreateCARequest; +import net.jami.jams.common.objects.user.AccessLevel; import net.jami.jams.common.utils.Validator; import java.io.IOException; @@ -37,11 +39,13 @@ import java.io.IOException; public class CreateCAServlet extends HttpServlet { @Override + @ScopedServletMethod(securityGroups = {AccessLevel.ADMIN}) protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { super.doGet(req, resp); } @Override + @ScopedServletMethod(securityGroups = {AccessLevel.ADMIN}) protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { CreateCARequest caRequest = JsonIterator.deserialize(req.getInputStream().readAllBytes(),CreateCARequest.class); if(!Validator.validateCARequests(caRequest)){ diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/install/CreateServerSettingsServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/install/CreateServerSettingsServlet.java index d7ac12d9..71ee86d1 100644 --- a/jams-server/src/main/java/net/jami/jams/server/servlets/api/install/CreateServerSettingsServlet.java +++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/install/CreateServerSettingsServlet.java @@ -28,7 +28,9 @@ import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import net.jami.jams.common.annotations.ScopedServletMethod; import net.jami.jams.common.cryptoengineapi.CertificateAuthorityConfig; +import net.jami.jams.common.objects.user.AccessLevel; import net.jami.jams.server.core.workflows.InstallationFinalizer; import java.io.IOException; @@ -39,11 +41,13 @@ public class CreateServerSettingsServlet extends HttpServlet { InstallationFinalizer finalizer = new InstallationFinalizer(); @Override + @ScopedServletMethod(securityGroups = {AccessLevel.ADMIN}) protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { super.doGet(req, resp); } @Override + @ScopedServletMethod(securityGroups = {AccessLevel.ADMIN}) protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException { CertificateAuthorityConfig config = JsonIterator.deserialize( req.getInputStream().readAllBytes(),CertificateAuthorityConfig.class); diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/install/InstallLastStepServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/install/InstallLastStepServlet.java index 1a9ce243..f16a24e7 100644 --- a/jams-server/src/main/java/net/jami/jams/server/servlets/api/install/InstallLastStepServlet.java +++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/install/InstallLastStepServlet.java @@ -28,6 +28,8 @@ import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import net.jami.jams.ca.JamsCA; +import net.jami.jams.common.annotations.ScopedServletMethod; +import net.jami.jams.common.objects.user.AccessLevel; import java.io.IOException; import java.util.HashMap; @@ -36,6 +38,7 @@ import java.util.HashMap; public class InstallLastStepServlet extends HttpServlet { @Override + @ScopedServletMethod(securityGroups = {AccessLevel.ADMIN}) protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { resp.setHeader("Access-Control-Allow-Origin", JamsCA.serverDomain); HashMap<String,String> payload = new HashMap<>(); diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/install/StartInstallServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/install/StartInstallServlet.java index 5475a954..424f47a5 100644 --- a/jams-server/src/main/java/net/jami/jams/server/servlets/api/install/StartInstallServlet.java +++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/install/StartInstallServlet.java @@ -32,8 +32,6 @@ import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import net.jami.jams.common.authentication.AuthenticationSourceType; import net.jami.jams.common.authmodule.AuthTokenResponse; -import net.jami.jams.common.dao.StatementElement; -import net.jami.jams.common.dao.StatementList; import net.jami.jams.common.objects.requests.CredentialsRequest; import net.jami.jams.common.objects.user.AccessLevel; import net.jami.jams.common.objects.user.User; @@ -43,6 +41,9 @@ import java.io.IOException; import static net.jami.jams.server.Server.dataStore; import static net.jami.jams.server.servlets.api.auth.login.AuthRequestProcessor.processUsernamePasswordAuth; +/* +This is not scoped because it is called once. + */ @Slf4j @WebServlet("/api/install/start") public class StartInstallServlet extends HttpServlet { diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/filters/AdminApiFilter.java b/jams-server/src/main/java/net/jami/jams/server/servlets/filters/AdminApiFilter.java deleted file mode 100644 index 29b41cca..00000000 --- a/jams-server/src/main/java/net/jami/jams/server/servlets/filters/AdminApiFilter.java +++ /dev/null @@ -1,86 +0,0 @@ -/* -* Copyright (C) 2020 by Savoir-faire Linux -* Authors: William Enright <william.enright@savoirfairelinux.com> -* Ndeye Anna Ndiaye <anna.ndiaye@savoirfairelinux.com> -* Johnny Flores <johnny.flores@savoirfairelinux.com> -* Mohammed Raza <mohammed.raza@savoirfairelinux.com> -* Felix Sidokhine <felix.sidokhine@savoirfairelinux.com> -* -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see <https://www.gnu.org/licenses/>. -*/ -package net.jami.jams.server.servlets.filters; - -import com.nimbusds.jose.JWSVerifier; -import com.nimbusds.jose.crypto.RSASSAVerifier; -import com.nimbusds.jwt.SignedJWT; -import jakarta.servlet.Filter; -import jakarta.servlet.FilterChain; -import jakarta.servlet.ServletException; -import jakarta.servlet.ServletRequest; -import jakarta.servlet.ServletResponse; -import jakarta.servlet.annotation.WebFilter; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import lombok.extern.slf4j.Slf4j; -import net.jami.jams.common.objects.user.AccessLevel; -import net.jami.jams.common.serialization.tomcat.TomcatCustomErrorHandler; -import net.jami.jams.server.Server; - -import java.io.IOException; -import java.util.HashSet; - -import static net.jami.jams.server.Server.userAuthenticationModule; -import static net.jami.jams.server.servlets.filters.JWTValidator.verifyLevel; -import static net.jami.jams.server.servlets.filters.JWTValidator.verifyValidity; - -@WebFilter(urlPatterns = {"/api/admin/*"}) -@Slf4j -public class AdminApiFilter implements Filter { - - private static final AccessLevel TARGET_LEVEL = AccessLevel.ADMIN; - - @Override - public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { - HttpServletRequest request = (HttpServletRequest) servletRequest; - HttpServletResponse response = (HttpServletResponse) servletResponse; - if (!Server.isInstalled.get()) { - TomcatCustomErrorHandler.sendCustomError(response,404,"Server is not installed yet!"); - } else { - boolean authsuccess = false; - boolean isLogin = false; - if (request.getServletPath().contains("login")) isLogin = true; - if(request.getHeader("authorization").contains("bearer") || request.getHeader("authorization").contains("Bearer")){ - SignedJWT signedJWT = null; - try { - JWSVerifier jwsVerifier = new RSASSAVerifier(userAuthenticationModule.getAuthModulePubKey()); - signedJWT = SignedJWT.parse(request.getHeader("authorization").replace("bearer","").replace("Bearer","")); - HashSet<AccessLevel> permissionLevels = new HashSet<>(); - permissionLevels.add(AccessLevel.ADMIN); - if(signedJWT.verify(jwsVerifier) && verifyValidity(signedJWT) && verifyLevel(signedJWT,permissionLevels)){ - authsuccess = true; - request.setAttribute("username",signedJWT.getJWTClaimsSet().getSubject()); - } - } catch (Exception e) { - log.info("Received an invalid token, declining access..."); - } - } - if (authsuccess || isLogin){ - filterChain.doFilter(servletRequest, servletResponse); - } - else TomcatCustomErrorHandler.sendCustomError(response,401,"You are not authenticated!"); - } - } - -} diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/filters/ApiFilter.java b/jams-server/src/main/java/net/jami/jams/server/servlets/filters/ApiFilter.java index 6e40c466..5d2384aa 100644 --- a/jams-server/src/main/java/net/jami/jams/server/servlets/filters/ApiFilter.java +++ b/jams-server/src/main/java/net/jami/jams/server/servlets/filters/ApiFilter.java @@ -23,9 +23,6 @@ package net.jami.jams.server.servlets.filters; -import com.nimbusds.jose.JWSVerifier; -import com.nimbusds.jose.crypto.RSASSAVerifier; -import com.nimbusds.jwt.SignedJWT; import jakarta.servlet.Filter; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; @@ -35,18 +32,18 @@ import jakarta.servlet.annotation.WebFilter; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; -import net.jami.jams.common.authmodule.AuthTokenResponse; -import net.jami.jams.common.objects.user.AccessLevel; import net.jami.jams.common.serialization.tomcat.TomcatCustomErrorHandler; import net.jami.jams.server.Server; import java.io.IOException; -import static net.jami.jams.server.Server.userAuthenticationModule; -import static net.jami.jams.server.servlets.api.auth.login.AuthRequestProcessor.processUsernamePasswordAuth; -import static net.jami.jams.server.servlets.filters.JWTValidator.verifyValidity; +import static net.jami.jams.server.servlets.filters.FilterUtils.doAuthCheck; -@WebFilter(urlPatterns = {"/api/auth/*"}) +/** + * Since we have the @ScopedServletMethod annotation, the admin filter became absolutely useless + * as we can simply scope things. + */ +@WebFilter(urlPatterns = {"/api/auth/*","/api/admin/*"}) @Slf4j public class ApiFilter implements Filter { @@ -59,42 +56,8 @@ public class ApiFilter implements Filter { } else { boolean authsuccess = false; boolean isLogin = false; - if (request.getServletPath().contains("login")) { - isLogin = true; - } - //We need to support 2 types of authorization tokens here: the basic and the bearer. - if(request.getHeader("authorization") != null){ - AuthTokenResponse res = null; - if(request.getHeader("authorization").contains("basic")) { - try { - res = processUsernamePasswordAuth(request.getHeader("authorization")); - SignedJWT token = SignedJWT.parse(res.getAccess_token()); - request.setAttribute("username", token.getJWTClaimsSet().getSubject()); - request.setAttribute("accessLevel", AccessLevel.valueOf(token.getJWTClaimsSet().getClaim("scope").toString())); - } catch (Exception e) { - log.error("Could not authenticate user!"); - } - if (res != null) authsuccess = true; - } - else if(request.getHeader("authorization").contains("bearer") || request.getHeader("authorization").contains("Bearer")){ - SignedJWT signedJWT = null; - try { - JWSVerifier jwsVerifier = new RSASSAVerifier(userAuthenticationModule.getAuthModulePubKey()); - signedJWT = SignedJWT.parse(request.getHeader("authorization").replace("bearer","").replace("Bearer","")); - //In this case, we need to ask the "target" resource what are the allowed access levels. - if (signedJWT.verify(jwsVerifier) && verifyValidity(signedJWT)) { - authsuccess = true; - request.setAttribute("username", signedJWT.getJWTClaimsSet().getSubject()); - if ((Boolean) signedJWT.getJWTClaimsSet().getClaim("oneTimePassword")) { - //TODO: use redirect to enforce the /changepassword url or something. - } - request.setAttribute("accessLevel", AccessLevel.valueOf(signedJWT.getJWTClaimsSet().getClaim("scope").toString())); - } - } catch (Exception e) { - log.info("Received an invalid token, declining access..."); - } - } - } + if (request.getServletPath().contains("login")) isLogin = true; + else authsuccess = doAuthCheck(request); if (authsuccess || isLogin) { filterChain.doFilter(servletRequest, servletResponse); } else { diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/filters/AuthRequestType.java b/jams-server/src/main/java/net/jami/jams/server/servlets/filters/AuthRequestType.java new file mode 100644 index 00000000..22457b4d --- /dev/null +++ b/jams-server/src/main/java/net/jami/jams/server/servlets/filters/AuthRequestType.java @@ -0,0 +1,8 @@ +package net.jami.jams.server.servlets.filters; + +public enum AuthRequestType { + FORM, //not supported on filters. + BASIC, + BEARER_TOKEN, + CLIENT_CERT //not supported on filters. +} diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/filters/FilterUtils.java b/jams-server/src/main/java/net/jami/jams/server/servlets/filters/FilterUtils.java new file mode 100644 index 00000000..6ae93786 --- /dev/null +++ b/jams-server/src/main/java/net/jami/jams/server/servlets/filters/FilterUtils.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Authors: William Enright <william.enright@savoirfairelinux.com> + * Ndeye Anna Ndiaye <anna.ndiaye@savoirfairelinux.com> + * Johnny Flores <johnny.flores@savoirfairelinux.com> + * Mohammed Raza <mohammed.raza@savoirfairelinux.com> + * Felix Sidokhine <felix.sidokhine@savoirfairelinux.com> + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +package net.jami.jams.server.servlets.filters; + +import com.nimbusds.jose.JWSVerifier; +import com.nimbusds.jose.crypto.RSASSAVerifier; +import com.nimbusds.jwt.SignedJWT; +import jakarta.servlet.http.HttpServletRequest; +import lombok.extern.slf4j.Slf4j; +import net.jami.jams.common.objects.user.AccessLevel; + +import java.util.Date; + +import static net.jami.jams.server.Server.userAuthenticationModule; +import static net.jami.jams.server.servlets.api.auth.login.AuthRequestProcessor.processUsernamePasswordAuth; + +@Slf4j +public class FilterUtils { + + public static final String USERNAME_ATTR = "username"; + public static final String ACCESS_LEVEL_ATTR = "accessLevel"; + + public static boolean verifyValidity(SignedJWT signedJWT) { + try { + return signedJWT.getJWTClaimsSet().getExpirationTime().compareTo(new Date()) > 0; + } catch (Exception e) { + return false; + } + } + + public static AuthRequestType classifyRequest(HttpServletRequest request) { + if (request.getHeader("authorization") != null) { + if (request.getHeader("authorization").contains("basic") || request.getHeader("authorization").contains("Basic")) { + return AuthRequestType.BASIC; + } + if (request.getHeader("authorization").contains("bearer") || request.getHeader("authorization").contains("Bearer")) { + return AuthRequestType.BEARER_TOKEN; + } + return null; + } + return null; + } + + public static boolean doAuthCheck(HttpServletRequest request){ + AuthRequestType requestType = FilterUtils.classifyRequest(request); + try { + if (requestType != null) { + SignedJWT token = null; + switch (requestType) { + case BASIC: + token = SignedJWT.parse(processUsernamePasswordAuth(request.getHeader("authorization")).getAccess_token()); + break; + case BEARER_TOKEN: + token = SignedJWT.parse(request.getHeader("authorization"). + replace("bearer", "") + .replace("Bearer", "")); + break; + default: + return false; + } + JWSVerifier jwsVerifier = new RSASSAVerifier(userAuthenticationModule.getAuthModulePubKey()); + if (token.verify(jwsVerifier) && verifyValidity(token)) { + request.setAttribute(USERNAME_ATTR, token.getJWTClaimsSet().getSubject()); + request.setAttribute(ACCESS_LEVEL_ATTR, AccessLevel.valueOf(token.getJWTClaimsSet().getClaim("scope").toString())); + return true; + } + } + return false; + } + catch (Exception e){ + log.info("Failed to process authentication request and denying access: {}",e.getMessage()); + return false; + } + } +} diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/filters/InstallFilter.java b/jams-server/src/main/java/net/jami/jams/server/servlets/filters/InstallFilter.java index b6213ab2..478fe16c 100644 --- a/jams-server/src/main/java/net/jami/jams/server/servlets/filters/InstallFilter.java +++ b/jams-server/src/main/java/net/jami/jams/server/servlets/filters/InstallFilter.java @@ -22,9 +22,6 @@ */ package net.jami.jams.server.servlets.filters; -import com.nimbusds.jose.JWSVerifier; -import com.nimbusds.jose.crypto.RSASSAVerifier; -import com.nimbusds.jwt.SignedJWT; import jakarta.servlet.Filter; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; @@ -38,9 +35,8 @@ import net.jami.jams.common.serialization.tomcat.TomcatCustomErrorHandler; import net.jami.jams.server.Server; import java.io.IOException; -import java.util.Date; -import static net.jami.jams.server.Server.userAuthenticationModule; +import static net.jami.jams.server.servlets.filters.FilterUtils.doAuthCheck; @WebFilter("/api/install/*") @Slf4j @@ -57,20 +53,7 @@ public class InstallFilter implements Filter { boolean authsuccess = false; boolean isLogin = false; if(request.getServletPath().contains("start")) isLogin = true; - SignedJWT signedJWT = null; - if(request.getHeader("authorization") != null && (request.getHeader("authorization").contains("bearer") || request.getHeader("authorization").contains("Bearer"))){ - try { - JWSVerifier jwsVerifier = new RSASSAVerifier(userAuthenticationModule.getAuthModulePubKey()); - signedJWT = SignedJWT.parse(request.getHeader("authorization").replace("bearer","").replace("Bearer","")); - if (signedJWT.verify(jwsVerifier) && signedJWT.getJWTClaimsSet().getExpirationTime().compareTo(new Date()) > 0) { - authsuccess = true; - request.setAttribute("username", signedJWT.getJWTClaimsSet().getSubject()); - request.setAttribute("accessLevel", signedJWT.getJWTClaimsSet().getClaim("scope")); - } - } catch (Exception e) { - log.info("Received an invalid token, declining access..."); - } - } + else authsuccess = doAuthCheck(request); if(authsuccess || isLogin) filterChain.doFilter(servletRequest,servletResponse); else TomcatCustomErrorHandler.sendCustomError(response,401,"You are not authenticated!"); } diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/filters/JWTValidator.java b/jams-server/src/main/java/net/jami/jams/server/servlets/filters/JWTValidator.java deleted file mode 100644 index 874f7666..00000000 --- a/jams-server/src/main/java/net/jami/jams/server/servlets/filters/JWTValidator.java +++ /dev/null @@ -1,50 +0,0 @@ -/* -* Copyright (C) 2020 by Savoir-faire Linux -* Authors: William Enright <william.enright@savoirfairelinux.com> -* Ndeye Anna Ndiaye <anna.ndiaye@savoirfairelinux.com> -* Johnny Flores <johnny.flores@savoirfairelinux.com> -* Mohammed Raza <mohammed.raza@savoirfairelinux.com> -* Felix Sidokhine <felix.sidokhine@savoirfairelinux.com> -* -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see <https://www.gnu.org/licenses/>. -*/ -package net.jami.jams.server.servlets.filters; - -import com.nimbusds.jwt.SignedJWT; -import net.jami.jams.common.objects.user.AccessLevel; - -import java.util.Date; -import java.util.Set; - -public class JWTValidator { - - public static boolean verifyLevel(SignedJWT signedJWT, Set<AccessLevel> targetLevels){ - try { - return targetLevels.contains(AccessLevel.valueOf(signedJWT.getJWTClaimsSet().getClaim("scope").toString())); - } - catch (Exception e){ - return false; - } - } - - public static boolean verifyValidity(SignedJWT signedJWT){ - try { - return signedJWT.getJWTClaimsSet().getExpirationTime().compareTo(new Date()) > 0; - } - catch (Exception e){ - return false; - } - } -} diff --git a/jams-server/src/main/resources/webapp/js/api.js b/jams-server/src/main/resources/webapp/js/api.js index 67ee6704..cb551e30 100644 --- a/jams-server/src/main/resources/webapp/js/api.js +++ b/jams-server/src/main/resources/webapp/js/api.js @@ -138,7 +138,7 @@ function setJWT(value) { function set_installation_response(url) { return function (data, statusCode, jqXHR) { - // endpoint does not exists + // endpoint does not exist - revoke if (data) { if (data.status == 404) { $('#installCompleteModalCenter').modal('show'); diff --git a/jams-server/src/main/resources/webapp/js/user.js b/jams-server/src/main/resources/webapp/js/user.js index 676ce2c5..917c3e26 100644 --- a/jams-server/src/main/resources/webapp/js/user.js +++ b/jams-server/src/main/resources/webapp/js/user.js @@ -149,7 +149,10 @@ $(document).ready(function() { deviceId = $('#revokeDeviceModal').attr("data-device-id"); isSearch = false; $('.loading').show(); - ajaxApiCall(api_path_delete_auth_device_revoke + "?deviceId=" + deviceId, 'DELETE', null, null, revokeDeviceHandler); + if (getAdminStatus()) + ajaxApiCall(api_path_delete_admin_device_revoke + "?deviceId=" + deviceId, 'DELETE', null, null, revokeDeviceHandler); + else + ajaxApiCall(api_path_delete_auth_device_revoke + "?deviceId=" + deviceId, 'DELETE', null, null, revokeDeviceHandler); }); $('.dismiss-device').on('click', function(){ @@ -346,7 +349,6 @@ function setUserDevices(data) { } function revokeUser(data, statusCode, jqXHR) { - if (jqXHR.status == 200) { $('#status-whatever').text("Revoked"); $('#status-whatever').addClass("text-danger"); diff --git a/ldap-connector/pom.xml b/ldap-connector/pom.xml index 9c87c3bc..f0a74061 100644 --- a/ldap-connector/pom.xml +++ b/ldap-connector/pom.xml @@ -23,8 +23,25 @@ <artifactId>ldaptive</artifactId> <version>${ldaptive.version}</version> </dependency> + <dependency> + <groupId>org.zapodot</groupId> + <artifactId>embedded-ldap-junit</artifactId> + <version>0.8.1</version> + <scope>test</scope> + </dependency> </dependencies> + <repositories> + <repository> + <id>forgerock</id> + <url>https://maven.forgerock.org/repo/community//</url> + </repository> + <repository> + <id>dcache</id> + <url>http://download.dcache.org/nexus/content/repositories/releases/</url> + </repository> + </repositories> + <build> <plugins> <plugin> diff --git a/ldap-connector/src/test/java/tests/GenericLDAPTest.java b/ldap-connector/src/test/java/tests/GenericLDAPTest.java new file mode 100644 index 00000000..be561469 --- /dev/null +++ b/ldap-connector/src/test/java/tests/GenericLDAPTest.java @@ -0,0 +1,61 @@ +package tests; + +import net.jami.jams.common.objects.user.UserProfile; +import net.jami.jams.ldap.connector.LDAPConnector; +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.zapodot.junit.ldap.EmbeddedLdapRule; +import org.zapodot.junit.ldap.EmbeddedLdapRuleBuilder; + +import java.io.InputStream; + +public class GenericLDAPTest { + + private static LDAPConnector ldapConnector = null; + + private void initLdapConnector() throws Exception{ + if(ldapConnector == null) { + InputStream inputStream = GenericLDAPTest.class.getClassLoader().getResourceAsStream("ldapconfig.json"); + ldapConnector = new LDAPConnector(new String(inputStream.readAllBytes())); + } + } + + @Rule + public EmbeddedLdapRule server = EmbeddedLdapRuleBuilder + .newInstance() + .usingDomainDsn("dc=savoirfairelinux,dc=net") + .bindingToPort(1089) + .importingLdifs("bootstrap.ldif") + .build(); + + @Test + public void testLookUp() throws Exception{ + initLdapConnector(); + UserProfile[] profiles = ldapConnector.getUserProfile("*","FULL_TEXT_NAME"); + Assertions.assertEquals(2,profiles.length); + Assertions.assertNotNull(profiles[0].getFirstName()); + Assertions.assertNotNull(profiles[1].getFirstName()); + } + + @Test + public void testAuth() throws Exception{ + boolean res; + initLdapConnector(); + res = ldapConnector.authenticate("fsidokhine","password"); + Assertions.assertTrue(res); + res = ldapConnector.authenticate("fsidokhine","badpassword"); + Assertions.assertFalse(res); + } + + @Test + public void getVcard() throws Exception{ + initLdapConnector(); + UserProfile[] profiles = ldapConnector.getUserProfile("Felix","FULL_TEXT_NAME"); + Assert.assertEquals(1,profiles.length); + String vcard = profiles[0].getAsVCard(); + Assert.assertNotNull(vcard); + } + +} diff --git a/ldap-connector/src/test/resources/bootstrap.ldif b/ldap-connector/src/test/resources/bootstrap.ldif new file mode 100644 index 00000000..cc1b2908 --- /dev/null +++ b/ldap-connector/src/test/resources/bootstrap.ldif @@ -0,0 +1,45 @@ +dn: dc=savoirfairelinux,dc=net +objectClass: top +objectClass: domain + +dn: ou=dsa,dc=savoirfairelinux,dc=net +ou: group +objectClass: top +objectclass: organizationalunit + +dn: cn=sipallow,ou=dsa,dc=savoirfairelinux,dc=net +objectClass: posixAccount +objectClass: inetorgperson +objectClass: organizationalperson +objectClass: top +uid: sipallow +userPassword: password + +dn: ou=users,dc=savoirfairelinux,dc=net +ou: people +objectClass: top +objectclass: organizationalunit + +dn: uid=fsidokhine,ou=users,dc=savoirfairelinux,dc=net +objectClass: posixAccount +objectClass: inetorgperson +objectClass: organizationalperson +objectClass: top +uid: fsidokhine +userPassword: password +givenName: Felix +cn: Felix Sidokhine +sn: Sidokhine +mail: felix.sidokhine@savoirfairelinux.com + +dn: uid=aberaud,ou=users,dc=savoirfairelinux,dc=net +objectClass: posixAccount +objectClass: inetorgperson +objectClass: organizationalperson +objectClass: top +uid: aberaud +userPassword: password +givenName: Adrien +cn: Adrien Beraud +sn: Beraud +mail: adrien.beraud@savoirfairelinux.com \ No newline at end of file diff --git a/ldap-connector/src/test/resources/ldapconfig.json b/ldap-connector/src/test/resources/ldapconfig.json new file mode 100644 index 00000000..39a9f61d --- /dev/null +++ b/ldap-connector/src/test/resources/ldapconfig.json @@ -0,0 +1,20 @@ +{ + "useStartTLS": false, + "realm": "savoirfairelinux", + "baseDN": "ou=users,dc=savoirfairelinux,dc=net", + "host": "ldap://localhost:1089", + "username": "cn=sipallow,ou=dsa,dc=savoirfairelinux,dc=net", + "password": "password", + "usernameField": "uid", + "fieldMappings": { + "givenName": "FirstName", + "sn": "LastName", + "jpegPhoto": "ProfilePicture", + "mail": "Email", + "telephoneNumber": "PhoneNumber", + "mobile": "MobileNumber", + "facsimileTelephoneNumber": "FaxNumber", + "extensionName": "PhoneNumberExtension", + "o": "Organization" + } +} \ No newline at end of file -- GitLab