diff --git a/hopsworks-IT/src/test/ruby/spec/conf_spec.rb b/hopsworks-IT/src/test/ruby/spec/conf_spec.rb
index f7b5de4f7b..e8566eb49e 100644
--- a/hopsworks-IT/src/test/ruby/spec/conf_spec.rb
+++ b/hopsworks-IT/src/test/ruby/spec/conf_spec.rb
@@ -50,7 +50,7 @@
expect(conf_dto['items'].length > 0).to be true
- hidden = conf_dto['items'].select {|c| c['name'].eql?("service_master_jwt")}
+ hidden = conf_dto['items'].select {|c| c['name'].eql?("int_service_api_key")}
expect(hidden[0]['hide']).to be true
end
diff --git a/hopsworks-IT/src/test/ruby/spec/jwt_spec.rb b/hopsworks-IT/src/test/ruby/spec/jwt_spec.rb
index 96316838da..69fac4c356 100644
--- a/hopsworks-IT/src/test/ruby/spec/jwt_spec.rb
+++ b/hopsworks-IT/src/test/ruby/spec/jwt_spec.rb
@@ -21,22 +21,6 @@
before :all do
reset_session
end
-
- context "#not logged in" do
- it "should not be able to renew service JWT" do
- put "#{ENV['HOPSWORKS_API']}/jwt/service", {
- token: "some_token",
- expiresAt: "1234",
- nbf: "1234"
- }
- expect_status_details(401)
- end
-
- it "should not be able to invalidate JWT" do
- delete "#{ENV['HOPSWORKS_API']}/jwt/service/some_token"
- expect_status_details(401)
- end
- end
context "#users" do
before :all do
@@ -82,72 +66,6 @@
refresh_variables
reset_session
end
-
- it "should be able to renew master jwt" do
-
- now = Time.now
- not_before = now.strftime("%Y-%m-%dT%H:%M:%S.%L%z")
- exp = now + 300
- new_expiration = exp.strftime("%Y-%m-%dT%H:%M:%S.%L%z")
-
- # Use one-time token
- Airborne.configure do |config|
- config.headers = {}
- config.headers["Authorization"] = "Bearer #{@renew_tokens[0]}"
- end
- sleep 1
- put "#{ENV['HOPSWORKS_API']}/jwt/service",
- {
- token: @master_token,
- expiresAt: new_expiration,
- nbf: not_before
- }
-
- expect_status_details(200)
-
- new_master_token = json_body[:jwt][:token]
- new_one_time_tokens = json_body[:renewTokens]
- expect(new_master_token).not_to be_nil
- expect(new_master_token).not_to be_empty
-
- expect(new_one_time_tokens.length).to eql(5)
-
- master_jwt = JWT.decode new_master_token, nil, false
-
- exp_response = Time.at(master_jwt[0]['exp'])
- nbf_response = Time.at(master_jwt[0]['nbf'])
- # Do not compare milliseconds, there might be different due to conversion
- expect(now.strftime("%Y-%m-%dT%H:%M:%S%z")).to eql(nbf_response.strftime("%Y-%m-%dT%H:%M:%S%z"))
- expect(exp.strftime("%Y-%m-%dT%H:%M:%S%z")).to eql(exp_response.strftime("%Y-%m-%dT%H:%M:%S%z"))
-
- # Previous token should still be valid
- Airborne.configure do |config|
- config.headers["Authorization"] = "Bearer #{@master_token}"
- end
- get "#{ENV['HOPSWORKS_CA']}/token"
- expect_status_details(200)
-
- # Invalidate previous master token
- Airborne.configure do |config|
- config.headers["Authorization"] = "Bearer #{new_master_token}"
- end
- delete "#{ENV['HOPSWORKS_API']}/jwt/service/#{@master_token}"
- expect_status_details(200)
-
- # Subsequent calls with the old master key should fail
- Airborne.configure do |config|
- config.headers["Authorization"] = "Bearer #{@master_token}"
- end
- get "#{ENV['HOPSWORKS_CA']}/token"
- expect_status_details(401)
-
- # But new master should be still valid...
- Airborne.configure do |config|
- config.headers["Authorization"] = "Bearer #{new_master_token}"
- end
- get "#{ENV['HOPSWORKS_CA']}/token"
- expect_status_details(200)
- end
end
end
diff --git a/hopsworks-api-auth/pom.xml b/hopsworks-api-auth/pom.xml
new file mode 100644
index 0000000000..434d252417
--- /dev/null
+++ b/hopsworks-api-auth/pom.xml
@@ -0,0 +1,78 @@
+
+
+
+ hopsworks
+ io.hops
+ 3.7.0-SNAPSHOT
+ ../pom.xml
+
+ 4.0.0
+
+ io.hops.hopsworks
+ hopsworks-api-auth
+ 3.7.0-SNAPSHOT
+ ejb
+ Hopsworks API authentication filters
+ hopsworks-api-auth
+
+
+ true
+
+
+
+
+ io.hops.hopsworks
+ hopsworks-persistence
+
+
+ io.hops.hopsworks
+ hopsworks-rest-utils
+
+
+ commons-codec
+ commons-codec
+
+
+
+
+ io.hops.hopsworks
+ hopsworks-jwt
+ ejb
+ provided
+
+
+ javax
+ javaee-api
+
+
+ commons-codec
+ commons-codec
+ 1.16.0
+
+
+
+ hopsworks-api-auth
+
+
+ org.apache.maven.plugins
+ maven-ejb-plugin
+
+
+
+
\ No newline at end of file
diff --git a/hopsworks-api-auth/src/main/java/io/hops/hopsworks/api/auth/Configuration.java b/hopsworks-api-auth/src/main/java/io/hops/hopsworks/api/auth/Configuration.java
new file mode 100644
index 0000000000..07f5c22b09
--- /dev/null
+++ b/hopsworks-api-auth/src/main/java/io/hops/hopsworks/api/auth/Configuration.java
@@ -0,0 +1,80 @@
+/*
+ * This file is part of Hopsworks
+ * Copyright (C) 2023, Hopsworks AB. All rights reserved
+ *
+ * Hopsworks is free software: you can redistribute it and/or modify it under the terms of
+ * the GNU Affero General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * Hopsworks 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License along with this program.
+ * If not, see .
+ */
+
+package io.hops.hopsworks.api.auth;
+
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import io.hops.hopsworks.persistence.entity.util.Variables;
+import io.hops.hopsworks.restutils.RESTLogLevel;
+
+import javax.annotation.PostConstruct;
+import javax.ejb.ConcurrencyManagement;
+import javax.ejb.ConcurrencyManagementType;
+import javax.ejb.Singleton;
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceContext;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+
+@Singleton
+@ConcurrencyManagement(ConcurrencyManagementType.BEAN)
+public class Configuration {
+
+ @PersistenceContext(unitName = "kthfsPU")
+ private EntityManager em;
+ private LoadingCache configuration;
+
+ public enum AuthConfigurationKeys {
+
+ HOPSWORKS_REST_LOG_LEVEL("hopsworks_rest_log_level", RESTLogLevel.PROD.name());
+
+ private String key;
+ private String defaultValue;
+
+ AuthConfigurationKeys(String key, String defaultValue) {
+ this.key = key;
+ this.defaultValue = defaultValue;
+ }
+ }
+
+ @PostConstruct
+ public void init() {
+ configuration = CacheBuilder.newBuilder()
+ .maximumSize(100)
+ .expireAfterWrite(10, TimeUnit.MINUTES)
+ .build(new CacheLoader() {
+ @Override
+ public String load(AuthConfigurationKeys k) throws Exception {
+ Variables var = em.find(Variables.class, k.key);
+ return var != null ? var.getValue() : k.defaultValue;
+ }
+ });
+ }
+
+ private String get(AuthConfigurationKeys key) {
+ try {
+ return configuration.get(key);
+ } catch (ExecutionException ex) {
+ return key.defaultValue;
+ }
+ }
+
+ public RESTLogLevel getLogLevel(AuthConfigurationKeys key) {
+ return RESTLogLevel.valueOf(get(key));
+ }
+}
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/filter/util/HopsworksSecurityContext.java b/hopsworks-api-auth/src/main/java/io/hops/hopsworks/api/auth/HopsworksSecurityContext.java
similarity index 96%
rename from hopsworks-api/src/main/java/io/hops/hopsworks/api/filter/util/HopsworksSecurityContext.java
rename to hopsworks-api-auth/src/main/java/io/hops/hopsworks/api/auth/HopsworksSecurityContext.java
index 4c5137dd0d..a1739a4e86 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/filter/util/HopsworksSecurityContext.java
+++ b/hopsworks-api-auth/src/main/java/io/hops/hopsworks/api/auth/HopsworksSecurityContext.java
@@ -13,21 +13,20 @@
* You should have received a copy of the GNU Affero General Public License along with this program.
* If not, see .
*/
-package io.hops.hopsworks.api.filter.util;
+package io.hops.hopsworks.api.auth;
import javax.ws.rs.core.SecurityContext;
import java.security.Principal;
public class HopsworksSecurityContext implements SecurityContext {
-
private final String scheme;
private final Subject subject;
-
+
public HopsworksSecurityContext(Subject subject, String scheme) {
this.scheme = scheme;
this.subject = subject;
}
-
+
@Override
public Principal getUserPrincipal() {
if (this.subject == null) {
@@ -35,7 +34,7 @@ public Principal getUserPrincipal() {
}
return () -> this.subject.getName();
}
-
+
@Override
public boolean isUserInRole(String role) {
if (this.subject.getRoles() != null && !this.subject.getRoles().isEmpty()) {
@@ -43,12 +42,12 @@ public boolean isUserInRole(String role) {
}
return false;
}
-
+
@Override
public boolean isSecure() {
return "https".equals(this.scheme);
}
-
+
@Override
public String getAuthenticationScheme() {
return SecurityContext.BASIC_AUTH;
diff --git a/hopsworks-api-auth/src/main/java/io/hops/hopsworks/api/auth/Secret.java b/hopsworks-api-auth/src/main/java/io/hops/hopsworks/api/auth/Secret.java
new file mode 100644
index 0000000000..a7c261e30b
--- /dev/null
+++ b/hopsworks-api-auth/src/main/java/io/hops/hopsworks/api/auth/Secret.java
@@ -0,0 +1,142 @@
+/*
+ * This file is part of Hopsworks
+ * Copyright (C) 2019, Logical Clocks AB. All rights reserved
+ *
+ * Hopsworks is free software: you can redistribute it and/or modify it under the terms of
+ * the GNU Affero General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * Hopsworks 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License along with this program.
+ * If not, see .
+ */
+package io.hops.hopsworks.api.auth;
+
+import org.apache.commons.codec.digest.DigestUtils;
+
+public class Secret {
+
+ private static final String KEY_ID_SEPARATOR = ".";
+ public static final String KEY_ID_SEPARATOR_REGEX = "\\.";
+ private final String prefix;
+ private final String secret;
+ private final String salt;
+ private final int prefixMinLength;
+ private final int secretMinLength;
+ private final int saltMinLength;
+ private final boolean prefixed;
+
+ public Secret(String prefix, String secret, String salt, int prefixMinLength, int secretMinLength,
+ int saltMinLength) {
+ this.prefix = prefix;
+ this.secret = secret;
+ this.salt = salt;
+ this.prefixMinLength = prefixMinLength;
+ this.secretMinLength = secretMinLength;
+ this.saltMinLength = saltMinLength;
+ this.prefixed = true;
+ }
+
+ public Secret(String prefix, String secret, String salt) {
+ this.prefix = prefix;
+ this.secret = secret;
+ this.salt = salt;
+ this.prefixMinLength = 0;
+ this.secretMinLength = 0;
+ this.saltMinLength = 0;
+ this.prefixed = true;
+ }
+
+ public Secret(String secret, String salt, int secretMinLength, int saltMinLength) {
+ this.prefix = "";
+ this.secret = secret;
+ this.salt = salt;
+ this.prefixMinLength = 0;
+ this.secretMinLength = secretMinLength;
+ this.saltMinLength = saltMinLength;
+ this.prefixed = false;
+ }
+
+ public Secret(String secret, String salt) {
+ this.prefix = "";
+ this.secret = secret;
+ this.salt = salt;
+ this.prefixMinLength = 0;
+ this.secretMinLength = 0;
+ this.saltMinLength = 0;
+ this.prefixed = false;
+ }
+
+ public String getPrefix() {
+ return prefix;
+ }
+
+ public String getSecret() {
+ return secret;
+ }
+
+ public String getSalt() {
+ return salt;
+ }
+
+ public String getSecretPlusSalt() {
+ return this.secret + this.salt;
+ }
+
+ public int getPrefixMinLength() {
+ return prefixMinLength;
+ }
+
+ public int getSecretMinLength() {
+ return secretMinLength;
+ }
+
+ public int getSaltMinLength() {
+ return saltMinLength;
+ }
+
+ public boolean isPrefixed() {
+ return prefixed;
+ }
+
+ public String getPrefixPlusSecret() {
+ return this.prefix + KEY_ID_SEPARATOR + this.secret;
+ }
+
+ public String getSha256HexDigest() {
+ String secPlusSalt = getSecretPlusSalt();
+ return DigestUtils.sha256Hex(secPlusSalt);
+ }
+
+ public String getSha512HexDigest() {
+ String secPlusSalt = getSecretPlusSalt();
+ return DigestUtils.sha512Hex(secPlusSalt);
+ }
+
+ public String getSha1HexDigest() {
+ String secPlusSalt = getSecretPlusSalt();
+ return DigestUtils.sha1Hex(secPlusSalt);
+ }
+
+ public boolean validateNotNullOrEmpty() {
+ if (this.prefixed) {
+ return this.prefix != null && !this.prefix.isEmpty() && this.secret != null && !this.secret.isEmpty() &&
+ this.salt != null && !this.salt.isEmpty();
+ } else {
+ return this.secret != null && !this.secret.isEmpty() && this.salt != null && !this.salt.isEmpty();
+ }
+ }
+
+ public boolean validateSize() {
+ boolean notNullOrEmpty = validateNotNullOrEmpty();
+ if (this.prefixed) {
+ return notNullOrEmpty && !(this.prefix.length() < prefixMinLength || this.secret.length() < secretMinLength ||
+ this.salt.length() < saltMinLength);
+ } else {
+ return notNullOrEmpty && !(this.secret.length() < secretMinLength || this.salt.length() < saltMinLength);
+ }
+ }
+}
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/filter/util/Subject.java b/hopsworks-api-auth/src/main/java/io/hops/hopsworks/api/auth/Subject.java
similarity index 95%
rename from hopsworks-api/src/main/java/io/hops/hopsworks/api/filter/util/Subject.java
rename to hopsworks-api-auth/src/main/java/io/hops/hopsworks/api/auth/Subject.java
index 458ff8d45a..e752d66d39 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/filter/util/Subject.java
+++ b/hopsworks-api-auth/src/main/java/io/hops/hopsworks/api/auth/Subject.java
@@ -13,31 +13,31 @@
* You should have received a copy of the GNU Affero General Public License along with this program.
* If not, see .
*/
-package io.hops.hopsworks.api.filter.util;
+package io.hops.hopsworks.api.auth;
import java.util.List;
public class Subject {
private String name;
private List roles;
-
+
public Subject(String name, List roles) {
this.name = name;
this.roles = roles;
}
-
+
public String getName() {
return name;
}
-
+
public void setName(String name) {
this.name = name;
}
-
+
public List getRoles() {
return roles;
}
-
+
public void setRoles(List roles) {
this.roles = roles;
}
diff --git a/hopsworks-common/src/main/java/io/hops/hopsworks/common/user/UserStatusValidator.java b/hopsworks-api-auth/src/main/java/io/hops/hopsworks/api/auth/UserStatusValidator.java
similarity index 61%
rename from hopsworks-common/src/main/java/io/hops/hopsworks/common/user/UserStatusValidator.java
rename to hopsworks-api-auth/src/main/java/io/hops/hopsworks/api/auth/UserStatusValidator.java
index f87ad9f717..5b82ee973b 100644
--- a/hopsworks-common/src/main/java/io/hops/hopsworks/common/user/UserStatusValidator.java
+++ b/hopsworks-api-auth/src/main/java/io/hops/hopsworks/api/auth/UserStatusValidator.java
@@ -1,9 +1,6 @@
/*
- * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b
- * are released under the following license:
- *
* This file is part of Hopsworks
- * Copyright (C) 2018, Logical Clocks AB. All rights reserved
+ * Copyright (C) 2023, Hopsworks AB. All rights reserved
*
* Hopsworks is free software: you can redistribute it and/or modify it under the terms of
* the GNU Affero General Public License as published by the Free Software Foundation,
@@ -15,33 +12,13 @@
*
* You should have received a copy of the GNU Affero General Public License along with this program.
* If not, see .
- *
- * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b
- * are released under the following license:
- *
- * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy of this
- * software and associated documentation files (the "Software"), to deal in the Software
- * without restriction, including without limitation the rights to use, copy, modify, merge,
- * publish, distribute, sublicense, and/or sell copies of the Software, and to permit
- * persons to whom the Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all copies or
- * substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
- * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
- * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
-package io.hops.hopsworks.common.user;
+package io.hops.hopsworks.api.auth;
+import io.hops.hopsworks.exceptions.UserException;
import io.hops.hopsworks.persistence.entity.user.security.ua.UserAccountStatus;
import io.hops.hopsworks.restutils.RESTCodes;
-import io.hops.hopsworks.exceptions.UserException;
import javax.ejb.Stateless;
import java.util.logging.Level;
@@ -67,7 +44,7 @@ public boolean checkStatus(UserAccountStatus status) throws UserException {
}
return true;
}
-
+
public void checkNewUserStatus(UserAccountStatus status) throws UserException {
switch (status) {
case NEW_MOBILE_ACCOUNT:
diff --git a/hopsworks-api-auth/src/main/java/io/hops/hopsworks/api/auth/UserUtilities.java b/hopsworks-api-auth/src/main/java/io/hops/hopsworks/api/auth/UserUtilities.java
new file mode 100644
index 0000000000..02f8abc5b3
--- /dev/null
+++ b/hopsworks-api-auth/src/main/java/io/hops/hopsworks/api/auth/UserUtilities.java
@@ -0,0 +1,37 @@
+/*
+ * This file is part of Hopsworks
+ * Copyright (C) 2023, Hopsworks AB. All rights reserved
+ *
+ * Hopsworks is free software: you can redistribute it and/or modify it under the terms of
+ * the GNU Affero General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * Hopsworks 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License along with this program.
+ * If not, see .
+ */
+
+package io.hops.hopsworks.api.auth;
+
+import io.hops.hopsworks.persistence.entity.user.BbcGroup;
+import io.hops.hopsworks.persistence.entity.user.Users;
+
+import javax.ejb.Stateless;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+@Stateless
+public class UserUtilities {
+ public List getUserRoles(Users p) {
+ Collection groupList = p.getBbcGroupCollection();
+ List list = new ArrayList<>();
+ for (BbcGroup g : groupList) {
+ list.add(g.getGroupName());
+ }
+ return list;
+ }
+}
diff --git a/hopsworks-common/src/main/java/io/hops/hopsworks/common/dao/user/security/apiKey/ApiKeyFacade.java b/hopsworks-api-auth/src/main/java/io/hops/hopsworks/api/auth/key/ApiKeyFacade.java
similarity index 89%
rename from hopsworks-common/src/main/java/io/hops/hopsworks/common/dao/user/security/apiKey/ApiKeyFacade.java
rename to hopsworks-api-auth/src/main/java/io/hops/hopsworks/api/auth/key/ApiKeyFacade.java
index 98e889668c..5de97cfaf4 100644
--- a/hopsworks-common/src/main/java/io/hops/hopsworks/common/dao/user/security/apiKey/ApiKeyFacade.java
+++ b/hopsworks-api-auth/src/main/java/io/hops/hopsworks/api/auth/key/ApiKeyFacade.java
@@ -1,6 +1,6 @@
/*
* This file is part of Hopsworks
- * Copyright (C) 2019, Logical Clocks AB. All rights reserved
+ * Copyright (C) 2023, Hopsworks AB. All rights reserved
*
* Hopsworks is free software: you can redistribute it and/or modify it under the terms of
* the GNU Affero General Public License as published by the Free Software Foundation,
@@ -13,11 +13,11 @@
* You should have received a copy of the GNU Affero General Public License along with this program.
* If not, see .
*/
-package io.hops.hopsworks.common.dao.user.security.apiKey;
+package io.hops.hopsworks.api.auth.key;
-import io.hops.hopsworks.common.dao.AbstractFacade;
import io.hops.hopsworks.persistence.entity.user.Users;
import io.hops.hopsworks.persistence.entity.user.security.apiKey.ApiKey;
+import io.hops.hopsworks.persistence.entity.util.AbstractFacade;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
@@ -31,64 +31,64 @@
@Stateless
public class ApiKeyFacade extends AbstractFacade {
-
+
@PersistenceContext(unitName = "kthfsPU")
private EntityManager em;
-
+
public ApiKeyFacade() {
super(ApiKey.class);
}
-
+
@Override
protected EntityManager getEntityManager() {
return em;
}
-
-
+
+
public ApiKey findByPrefix(String prefix) {
TypedQuery query = em.createNamedQuery("ApiKey.findByPrefix",
- ApiKey.class).setParameter("prefix", prefix);
+ ApiKey.class).setParameter("prefix", prefix);
try {
return query.getSingleResult();
} catch (NoResultException e) {
return null;
}
}
-
+
public ApiKey findByUserAndName(Users user, String name) {
TypedQuery query = em.createNamedQuery("ApiKey.findByUserAndName",
- ApiKey.class).setParameter("user", user).setParameter("name", name);
+ ApiKey.class).setParameter("user", user).setParameter("name", name);
try {
return query.getSingleResult();
} catch (NoResultException e) {
return null;
}
}
-
+
public List findByUser(Users user) {
TypedQuery query = em.createNamedQuery("ApiKey.findByUser",
- ApiKey.class).setParameter("user", user);
+ ApiKey.class).setParameter("user", user);
return query.getResultList();
}
-
+
public CollectionInfo findByUser(Integer offset, Integer limit, Set extends FilterBy> filter,
- Set extends SortBy> sort, Users user) {
+ Set extends SortBy> sort, Users user) {
String queryStr = buildQuery("SELECT a FROM ApiKey a ", filter, sort, "a.user = :user AND a.reserved = 0");
String queryCountStr = buildQuery("SELECT COUNT(a.id) FROM ApiKey a ", filter, sort, "a.user = :user " +
- "AND a.reserved = 0");
+ "AND a.reserved = 0");
Query query = em.createQuery(queryStr, ApiKey.class).setParameter("user", user);
Query queryCount = em.createQuery(queryCountStr, ApiKey.class).setParameter("user", user);
return findAll(offset, limit, filter, query, queryCount);
}
-
+
private CollectionInfo findAll(Integer offset, Integer limit,
- Set extends AbstractFacade.FilterBy> filter, Query query, Query queryCount) {
+ Set extends AbstractFacade.FilterBy> filter, Query query, Query queryCount) {
setFilter(filter, query);
setFilter(filter, queryCount);
setOffsetAndLim(offset, limit, query);
return new CollectionInfo((Long) queryCount.getSingleResult(), query.getResultList());
}
-
+
private void setFilter(Set extends AbstractFacade.FilterBy> filters, Query q) {
if (filters == null || filters.isEmpty()) {
return;
@@ -97,7 +97,7 @@ private void setFilter(Set extends AbstractFacade.FilterBy> filters, Query q)
setFilterQuery(filter, q);
}
}
-
+
private void setFilterQuery(FilterBy filter, Query q) {
switch (Filters.valueOf(filter.getValue())) {
case NAME:
@@ -114,48 +114,48 @@ private void setFilterQuery(FilterBy filter, Query q) {
break;
default:
break;
-
+
}
}
-
+
public enum Sorts {
NAME("NAME", "a.name ", "ASC"),
MODIFIED("MODIFIED", "a.modified ", "ASC"),
CREATED("CREATED", "a.created ", "ASC");
-
+
private final String value;
private final String sql;
private final String defaultParam;
-
+
private Sorts(String value, String sql, String defaultParam) {
this.value = value;
this.sql = sql;
this.defaultParam = defaultParam;
}
-
+
public String getValue() {
return value;
}
-
+
public String getSql() {
return sql;
}
-
+
public String getDefaultParam() {
return defaultParam;
}
-
+
public String getJoin() {
return null;
}
-
+
@Override
public String toString() {
return value;
}
-
+
}
-
+
public enum Filters {
NAME("NAME", "a.name IN :name ", "name", " "),
MODIFIED("MODIFIED", "a.modified = :modified ", "modified", "1970-01-01T00:00:00.000"),
@@ -164,39 +164,40 @@ public enum Filters {
CREATED("CREATED", "a.created = :created ", "created", "1970-01-01T00:00:00.000"),
CREATED_LT("CREATED_LT", "a.created < :created_lt ", "created_lt", "1970-01-01T00:00:00.000"),
CREATED_GT("CREATED_GT", "a.created > :created_gt ", "created_gt", "1970-01-01T00:00:00.000");
-
+
private final String value;
private final String sql;
private final String field;
private final String defaultParam;
-
+
private Filters(String value, String sql, String field, String defaultParam) {
this.value = value;
this.sql = sql;
this.field = field;
this.defaultParam = defaultParam;
}
-
+
public String getDefaultParam() {
return defaultParam;
}
-
+
public String getValue() {
return value;
}
-
+
public String getSql() {
return sql;
}
-
+
public String getField() {
return field;
}
-
+
@Override
public String toString() {
return value;
}
-
+
}
}
+
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/filter/apiKey/ApiKeyFilter.java b/hopsworks-api-auth/src/main/java/io/hops/hopsworks/api/auth/key/ApiKeyFilter.java
similarity index 75%
rename from hopsworks-api/src/main/java/io/hops/hopsworks/api/filter/apiKey/ApiKeyFilter.java
rename to hopsworks-api-auth/src/main/java/io/hops/hopsworks/api/auth/key/ApiKeyFilter.java
index 3ceeb3e926..846cbce6ec 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/filter/apiKey/ApiKeyFilter.java
+++ b/hopsworks-api-auth/src/main/java/io/hops/hopsworks/api/auth/key/ApiKeyFilter.java
@@ -1,6 +1,6 @@
/*
* This file is part of Hopsworks
- * Copyright (C) 2019, Logical Clocks AB. All rights reserved
+ * Copyright (C) 2023, Hopsworks AB. All rights reserved
*
* Hopsworks is free software: you can redistribute it and/or modify it under the terms of
* the GNU Affero General Public License as published by the Free Software Foundation,
@@ -13,15 +13,10 @@
* You should have received a copy of the GNU Affero General Public License along with this program.
* If not, see .
*/
-package io.hops.hopsworks.api.filter.apiKey;
+package io.hops.hopsworks.api.auth.key;
-import io.hops.hopsworks.api.filter.util.HopsworksSecurityContext;
-import io.hops.hopsworks.api.filter.util.Subject;
-import io.hops.hopsworks.api.util.RESTApiJsonResponse;
-import io.hops.hopsworks.common.user.UserStatusValidator;
-import io.hops.hopsworks.common.user.UsersController;
-import io.hops.hopsworks.common.user.security.apiKey.ApiKeyController;
-import io.hops.hopsworks.common.util.Settings;
+import io.hops.hopsworks.api.auth.HopsworksSecurityContext;
+import io.hops.hopsworks.api.auth.Subject;
import io.hops.hopsworks.exceptions.ApiKeyException;
import io.hops.hopsworks.exceptions.UserException;
import io.hops.hopsworks.jwt.annotation.JWTRequired;
@@ -29,18 +24,14 @@
import io.hops.hopsworks.persistence.entity.user.security.apiKey.ApiKey;
import io.hops.hopsworks.persistence.entity.user.security.apiKey.ApiScope;
import io.hops.hopsworks.restutils.JsonResponse;
+import io.hops.hopsworks.restutils.RESTApiJsonResponse;
import io.hops.hopsworks.restutils.RESTCodes;
+import io.hops.hopsworks.restutils.RESTLogLevel;
-import javax.annotation.Priority;
-import javax.ejb.EJB;
-import javax.ws.rs.Priorities;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
-import javax.ws.rs.container.ResourceInfo;
-import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Response;
-import javax.ws.rs.ext.Provider;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collections;
@@ -53,27 +44,11 @@
import static io.hops.hopsworks.jwt.Constants.BEARER;
import static io.hops.hopsworks.jwt.Constants.WWW_AUTHENTICATE_VALUE;
-@Provider
-@ApiKeyRequired
-@Priority(Priorities.AUTHENTICATION - 1)
-public class ApiKeyFilter implements ContainerRequestFilter {
- private static final Logger LOGGER = Logger.getLogger(ApiKeyFilter.class.getName());
- private static final String WWW_AUTHENTICATE_VALUE = "ApiKey realm=\"Cauth Realm\"";
+public abstract class ApiKeyFilter implements ContainerRequestFilter {
+ private static final Logger LOGGER = Logger.getLogger(ApiKeyFilter.class.getName());
public static final String API_KEY = "ApiKey ";
-
- @EJB
- private ApiKeyController apiKeyController;
- @EJB
- private UsersController usersController;
- @EJB
- private UserStatusValidator userStatusValidator;
- @EJB
- private Settings settings;
-
- @Context
- private ResourceInfo resourceInfo;
-
+
@Override
public void filter(ContainerRequestContext requestContext) {
String authorizationHeader = requestContext.getHeaderString(HttpHeaders.AUTHORIZATION);
@@ -83,7 +58,7 @@ public void filter(ContainerRequestContext requestContext) {
jsonResponse.setErrorCode(RESTCodes.SecurityErrorCode.EJB_ACCESS_LOCAL.getCode());
jsonResponse.setErrorMsg("Authorization header not set.");
requestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED).header(HttpHeaders.WWW_AUTHENTICATE,
- WWW_AUTHENTICATE_VALUE).entity(jsonResponse).build());
+ WWW_AUTHENTICATE_VALUE).entity(jsonResponse).build());
return;
}
if (authorizationHeader.startsWith(BEARER)) {
@@ -92,7 +67,7 @@ public void filter(ContainerRequestContext requestContext) {
jsonResponse.setErrorCode(RESTCodes.SecurityErrorCode.EJB_ACCESS_LOCAL.getCode());
jsonResponse.setErrorMsg("Authorization method not supported.");
requestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED).header(HttpHeaders.WWW_AUTHENTICATE,
- WWW_AUTHENTICATE_VALUE).entity(jsonResponse).build());
+ WWW_AUTHENTICATE_VALUE).entity(jsonResponse).build());
}
return;
}
@@ -101,17 +76,17 @@ public void filter(ContainerRequestContext requestContext) {
jsonResponse.setErrorCode(RESTCodes.SecurityErrorCode.EJB_ACCESS_LOCAL.getCode());
jsonResponse.setErrorMsg("Invalidated Api key.");
requestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED).header(HttpHeaders.WWW_AUTHENTICATE,
- WWW_AUTHENTICATE_VALUE).entity(jsonResponse).build());
+ WWW_AUTHENTICATE_VALUE).entity(jsonResponse).build());
return;
}
-
+
String key = authorizationHeader.substring(API_KEY.length()).trim();
try {
- ApiKey apiKey = apiKeyController.getApiKey(key);
+ ApiKey apiKey = getApiKey(key);
Users user = apiKey.getUser();
- userStatusValidator.checkStatus(user.getStatus());
- List roles = usersController.getUserRoles(user);
- Set scopes = apiKeyController.getScopes(apiKey);
+ validateUserStatus(user);
+ List roles = getUserRoles(user);
+ Set scopes = getApiScopes(apiKey);
checkRole(roles);
checkScope(scopes);
Subject subject = new Subject(user.getUsername(), roles);
@@ -119,13 +94,20 @@ public void filter(ContainerRequestContext requestContext) {
requestContext.setSecurityContext(new HopsworksSecurityContext(subject, scheme));
} catch (ApiKeyException | UserException e) {
LOGGER.log(Level.FINEST, "Api key Verification Exception: {0}", e.getMessage());
- e.buildJsonResponse(jsonResponse, settings.getHopsworksRESTLogLevel());
+ e.buildJsonResponse(jsonResponse, getRestLogLevel());
requestContext.abortWith(Response.status(e.getErrorCode().getRespStatus().getStatusCode())
- .header(HttpHeaders.WWW_AUTHENTICATE, WWW_AUTHENTICATE_VALUE).entity(jsonResponse).build());
+ .header(HttpHeaders.WWW_AUTHENTICATE, WWW_AUTHENTICATE_VALUE).entity(jsonResponse).build());
}
}
-
-
+
+ protected abstract void validateUserStatus(Users user) throws UserException;
+ protected abstract ApiKey getApiKey(String key) throws ApiKeyException;
+ protected abstract Set getApiScopes(ApiKey key);
+ protected abstract List getUserRoles(Users user);
+ protected abstract RESTLogLevel getRestLogLevel();
+ protected abstract Class> getResourceClass();
+ protected abstract Method getResourceMethod();
+
private void checkRole(List userRoles) throws ApiKeyException {
Set annotationRoles = getAllowedRoles();
if (annotationRoles.isEmpty() || userRoles == null || userRoles.isEmpty()) {
@@ -136,7 +118,7 @@ private void checkRole(List userRoles) throws ApiKeyException {
throw new ApiKeyException(RESTCodes.ApiKeyErrorCode.KEY_ROLE_CONTROL_EXCEPTION, Level.FINE);
}
}
-
+
private void checkScope(Set scopes) throws ApiKeyException {
Set annotationScopes = getAllowedScopes();
if (annotationScopes.isEmpty() || scopes == null || scopes.isEmpty()) {
@@ -147,7 +129,7 @@ private void checkScope(Set scopes) throws ApiKeyException {
throw new ApiKeyException(RESTCodes.ApiKeyErrorCode.KEY_SCOPE_CONTROL_EXCEPTION, Level.FINE);
}
}
-
+
private Set getAllowedRoles() {
ApiKeyRequired rolesAnnotation = getAnnotation();
if (rolesAnnotation == null) {
@@ -155,7 +137,7 @@ private Set getAllowedRoles() {
}
return new HashSet<>(Arrays.asList(rolesAnnotation.allowedUserRoles()));
}
-
+
private Set getAllowedScopes() {
ApiKeyRequired scopeAnnotation = getAnnotation();
if (scopeAnnotation == null) {
@@ -163,18 +145,18 @@ private Set getAllowedScopes() {
}
return new HashSet<>(Arrays.asList(scopeAnnotation.acceptedScopes()));
}
-
+
private ApiKeyRequired getAnnotation() {
- Class> resourceClass = resourceInfo.getResourceClass();
- Method method = resourceInfo.getResourceMethod();
+ Class> resourceClass = getResourceClass();
+ Method method = getResourceMethod();
ApiKeyRequired methodRolesAnnotation = method.getAnnotation(ApiKeyRequired.class);
ApiKeyRequired classRolesAnnotation = resourceClass.getAnnotation(ApiKeyRequired.class);
return methodRolesAnnotation != null ? methodRolesAnnotation : classRolesAnnotation;
}
-
+
private JWTRequired getJWTAnnotation() {
- Class> resourceClass = resourceInfo.getResourceClass();
- Method method = resourceInfo.getResourceMethod();
+ Class> resourceClass = getResourceClass();
+ Method method = getResourceMethod();
JWTRequired methodAcceptedTokens = method.getAnnotation(JWTRequired.class);
JWTRequired classAcceptedTokens = resourceClass.getAnnotation(JWTRequired.class);
JWTRequired annotation = methodAcceptedTokens != null ? methodAcceptedTokens : classAcceptedTokens;
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/filter/apiKey/ApiKeyRequired.java b/hopsworks-api-auth/src/main/java/io/hops/hopsworks/api/auth/key/ApiKeyRequired.java
similarity index 96%
rename from hopsworks-api/src/main/java/io/hops/hopsworks/api/filter/apiKey/ApiKeyRequired.java
rename to hopsworks-api-auth/src/main/java/io/hops/hopsworks/api/auth/key/ApiKeyRequired.java
index b69584b8d5..6fce6ffb5d 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/filter/apiKey/ApiKeyRequired.java
+++ b/hopsworks-api-auth/src/main/java/io/hops/hopsworks/api/auth/key/ApiKeyRequired.java
@@ -13,7 +13,7 @@
* You should have received a copy of the GNU Affero General Public License along with this program.
* If not, see .
*/
-package io.hops.hopsworks.api.filter.apiKey;
+package io.hops.hopsworks.api.auth.key;
import io.hops.hopsworks.persistence.entity.user.security.apiKey.ApiScope;
diff --git a/hopsworks-common/src/main/java/io/hops/hopsworks/common/dao/user/security/apiKey/ApiKeyScopeFacade.java b/hopsworks-api-auth/src/main/java/io/hops/hopsworks/api/auth/key/ApiKeyScopeFacade.java
similarity index 86%
rename from hopsworks-common/src/main/java/io/hops/hopsworks/common/dao/user/security/apiKey/ApiKeyScopeFacade.java
rename to hopsworks-api-auth/src/main/java/io/hops/hopsworks/api/auth/key/ApiKeyScopeFacade.java
index 1fcb0ade20..4970ab41c3 100644
--- a/hopsworks-common/src/main/java/io/hops/hopsworks/common/dao/user/security/apiKey/ApiKeyScopeFacade.java
+++ b/hopsworks-api-auth/src/main/java/io/hops/hopsworks/api/auth/key/ApiKeyScopeFacade.java
@@ -1,6 +1,6 @@
/*
* This file is part of Hopsworks
- * Copyright (C) 2019, Logical Clocks AB. All rights reserved
+ * Copyright (C) 2023, Hopsworks AB. All rights reserved
*
* Hopsworks is free software: you can redistribute it and/or modify it under the terms of
* the GNU Affero General Public License as published by the Free Software Foundation,
@@ -13,10 +13,10 @@
* You should have received a copy of the GNU Affero General Public License along with this program.
* If not, see .
*/
-package io.hops.hopsworks.common.dao.user.security.apiKey;
+package io.hops.hopsworks.api.auth.key;
-import io.hops.hopsworks.common.dao.AbstractFacade;
import io.hops.hopsworks.persistence.entity.user.security.apiKey.ApiKeyScope;
+import io.hops.hopsworks.persistence.entity.util.AbstractFacade;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
@@ -24,17 +24,15 @@
@Stateless
public class ApiKeyScopeFacade extends AbstractFacade {
-
@PersistenceContext(unitName = "kthfsPU")
private EntityManager em;
-
+
public ApiKeyScopeFacade() {
super(ApiKeyScope.class);
}
-
+
@Override
protected EntityManager getEntityManager() {
return em;
}
-
}
diff --git a/hopsworks-api-auth/src/main/java/io/hops/hopsworks/api/auth/key/ApiKeyUtilities.java b/hopsworks-api-auth/src/main/java/io/hops/hopsworks/api/auth/key/ApiKeyUtilities.java
new file mode 100644
index 0000000000..cdb3c41884
--- /dev/null
+++ b/hopsworks-api-auth/src/main/java/io/hops/hopsworks/api/auth/key/ApiKeyUtilities.java
@@ -0,0 +1,63 @@
+/*
+ * This file is part of Hopsworks
+ * Copyright (C) 2023, Hopsworks AB. All rights reserved
+ *
+ * Hopsworks is free software: you can redistribute it and/or modify it under the terms of
+ * the GNU Affero General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * Hopsworks 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License along with this program.
+ * If not, see .
+ */
+package io.hops.hopsworks.api.auth.key;
+
+import io.hops.hopsworks.api.auth.Secret;
+import io.hops.hopsworks.exceptions.ApiKeyException;
+import io.hops.hopsworks.persistence.entity.user.security.apiKey.ApiKey;
+import io.hops.hopsworks.persistence.entity.user.security.apiKey.ApiKeyScope;
+import io.hops.hopsworks.persistence.entity.user.security.apiKey.ApiScope;
+import io.hops.hopsworks.restutils.RESTCodes;
+
+import javax.ejb.EJB;
+import javax.ejb.Stateless;
+import javax.ejb.TransactionAttribute;
+import javax.ejb.TransactionAttributeType;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.logging.Level;
+
+@Stateless
+@TransactionAttribute(TransactionAttributeType.NEVER)
+public class ApiKeyUtilities {
+ @EJB
+ private ApiKeyFacade apiKeyFacade;
+
+ public ApiKey getApiKey(String key) throws ApiKeyException {
+ String[] parts = key.split(Secret.KEY_ID_SEPARATOR_REGEX);
+ if (parts.length < 2) {
+ throw new ApiKeyException(RESTCodes.ApiKeyErrorCode.KEY_INVALID, Level.FINE);
+ }
+ ApiKey apiKey = apiKeyFacade.findByPrefix(parts[0]);
+ if (apiKey == null) {
+ throw new ApiKeyException(RESTCodes.ApiKeyErrorCode.KEY_NOT_FOUND_IN_DATABASE, Level.FINE);
+ }
+ //___MinLength can be set to 0 b/c no validation is needed if the key was in db
+ Secret secret = new Secret(parts[0], parts[1], apiKey.getSalt());
+ if (!secret.getSha256HexDigest().equals(apiKey.getSecret())) {
+ throw new ApiKeyException(RESTCodes.ApiKeyErrorCode.KEY_INVALID, Level.FINE);
+ }
+ return apiKey;
+ }
+
+ public Set getScopes(ApiKey apiKey) {
+ Set scopes = new HashSet<>();
+ for (ApiKeyScope scope : apiKey.getApiKeyScopeCollection()) {
+ scopes.add(scope.getScope());
+ }
+ return scopes;
+ }
+}
diff --git a/hopsworks-api/pom.xml b/hopsworks-api/pom.xml
index 2fb8a1d0f6..1c48632be4 100644
--- a/hopsworks-api/pom.xml
+++ b/hopsworks-api/pom.xml
@@ -113,6 +113,20 @@
provided
+
+ io.hops.hopsworks
+ hopsworks-api-auth
+ ejb
+
+
+
+ *
+ *
+
+
+ provided
+
+
com.fasterxml.jackson.datatype
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/admin/SystemAdminService.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/admin/SystemAdminService.java
index bc1504e671..915d318266 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/admin/SystemAdminService.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/admin/SystemAdminService.java
@@ -51,7 +51,6 @@
import io.hops.hopsworks.common.dao.kafka.TopicDefaultValueDTO;
import io.hops.hopsworks.common.kafka.KafkaController;
import io.hops.hopsworks.common.security.CertificatesMgmService;
-import io.hops.hopsworks.common.security.ServiceJWTKeepAlive;
import io.hops.hopsworks.common.util.Settings;
import io.hops.hopsworks.exceptions.FeaturestoreException;
import io.hops.hopsworks.exceptions.OpenSearchException;
@@ -59,7 +58,6 @@
import io.hops.hopsworks.exceptions.HopsSecurityException;
import io.hops.hopsworks.exceptions.KafkaException;
import io.hops.hopsworks.jwt.annotation.JWTRequired;
-import io.hops.hopsworks.jwt.exception.JWTException;
import io.hops.hopsworks.persistence.entity.user.Users;
import io.hops.hopsworks.persistence.entity.util.Variables;
import io.hops.hopsworks.restutils.RESTCodes;
@@ -108,8 +106,6 @@ public class SystemAdminService {
@EJB
private JWTHelper jWTHelper;
@EJB
- private ServiceJWTKeepAlive serviceJWTKeepAlive;
- @EJB
private KafkaController kafkaController;
@EJB
private SearchFSReindexer searchFSReindexer;
@@ -193,23 +189,6 @@ public Response updateVariables(VariablesRequest variablesRequest, @Context Secu
RESTApiJsonResponse response = noCacheResponse.buildJsonResponse(Response.Status.NO_CONTENT, "Variables updated");
return noCacheResponse.getNoCacheResponseBuilder(Response.Status.OK).entity(response).build();
}
-
- @POST
- @Path("/rotate")
- public Response serviceKeyRotate(@Context SecurityContext sc) {
- certificatesMgmService.issueServiceKeyRotationCommand();
- RESTApiJsonResponse
- response = noCacheResponse.buildJsonResponse(Response.Status.NO_CONTENT, "Key rotation commands " +
- "issued");
- return noCacheResponse.getNoCacheResponseBuilder(Response.Status.NO_CONTENT).entity(response).build();
- }
-
- @PUT
- @Path("/servicetoken")
- public Response renewServiceJWT(@Context SecurityContext sc) throws JWTException {
- serviceJWTKeepAlive.forceRenewServiceToken();
- return Response.noContent().build();
- }
@ApiOperation(value = "Get kafka system settings")
@GET
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/admin/UsersAdminResource.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/admin/UsersAdminResource.java
index 0073f62a83..437f16c488 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/admin/UsersAdminResource.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/admin/UsersAdminResource.java
@@ -19,7 +19,7 @@
import com.google.common.base.Strings;
import io.hops.hopsworks.api.admin.dto.NewUserDTO;
import io.hops.hopsworks.api.filter.Audience;
-import io.hops.hopsworks.api.filter.apiKey.ApiKeyRequired;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.api.jwt.JWTHelper;
import io.hops.hopsworks.api.user.BbcGroupDTO;
import io.hops.hopsworks.api.user.UserIds;
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/admin/cloud/CloudRoleMappingResource.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/admin/cloud/CloudRoleMappingResource.java
index bc5007bd6c..4bc7414863 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/admin/cloud/CloudRoleMappingResource.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/admin/cloud/CloudRoleMappingResource.java
@@ -15,8 +15,8 @@
*/
package io.hops.hopsworks.api.admin.cloud;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.api.filter.Audience;
-import io.hops.hopsworks.api.filter.apiKey.ApiKeyRequired;
import io.hops.hopsworks.jwt.annotation.JWTRequired;
import io.hops.hopsworks.persistence.entity.user.security.apiKey.ApiScope;
import io.swagger.annotations.Api;
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/admin/conf/ConfigurationResource.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/admin/conf/ConfigurationResource.java
index d76cbe37f3..49e648f863 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/admin/conf/ConfigurationResource.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/admin/conf/ConfigurationResource.java
@@ -17,7 +17,7 @@
package io.hops.hopsworks.api.admin.conf;
import io.hops.hopsworks.api.filter.Audience;
-import io.hops.hopsworks.api.filter.apiKey.ApiKeyRequired;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.common.api.ResourceRequest;
import io.hops.hopsworks.common.dao.util.VariablesFacade;
import io.hops.hopsworks.common.util.Settings;
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/admin/projects/ProjectsAdminResource.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/admin/projects/ProjectsAdminResource.java
index facf6de85a..ae3a8ea521 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/admin/projects/ProjectsAdminResource.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/admin/projects/ProjectsAdminResource.java
@@ -17,7 +17,7 @@
import io.hops.hopsworks.api.admin.dto.ProjectAdminInfoDTO;
import io.hops.hopsworks.api.filter.Audience;
-import io.hops.hopsworks.api.filter.apiKey.ApiKeyRequired;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.api.jwt.JWTHelper;
import io.hops.hopsworks.common.api.ResourceRequest;
import io.hops.hopsworks.common.dao.project.ProjectFacade;
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/dataset/DatasetResource.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/dataset/DatasetResource.java
index ea5c44fef7..5aa8d9f827 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/dataset/DatasetResource.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/dataset/DatasetResource.java
@@ -21,7 +21,7 @@
import io.hops.hopsworks.api.dataset.tags.DatasetTagsResource;
import io.hops.hopsworks.api.filter.AllowedProjectRoles;
import io.hops.hopsworks.api.filter.Audience;
-import io.hops.hopsworks.api.filter.apiKey.ApiKeyRequired;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.api.jwt.JWTHelper;
import io.hops.hopsworks.api.util.DownloadService;
import io.hops.hopsworks.api.util.Pagination;
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/dataset/tags/DatasetTagsResource.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/dataset/tags/DatasetTagsResource.java
index c88ca3c7a9..a6e2597217 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/dataset/tags/DatasetTagsResource.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/dataset/tags/DatasetTagsResource.java
@@ -21,7 +21,7 @@
import io.hops.hopsworks.api.tags.TagsExpansionBeanParam;
import io.hops.hopsworks.api.filter.AllowedProjectRoles;
import io.hops.hopsworks.api.filter.Audience;
-import io.hops.hopsworks.api.filter.apiKey.ApiKeyRequired;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.api.jwt.JWTHelper;
import io.hops.hopsworks.common.api.ResourceRequest;
import io.hops.hopsworks.common.dataset.util.DatasetHelper;
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/experiments/ExperimentsBuilder.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/experiments/ExperimentsBuilder.java
index 579339e6e9..f77acd5f71 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/experiments/ExperimentsBuilder.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/experiments/ExperimentsBuilder.java
@@ -40,9 +40,9 @@
import io.hops.hopsworks.exceptions.DatasetException;
import io.hops.hopsworks.exceptions.ExperimentsException;
import io.hops.hopsworks.exceptions.GenericException;
-import io.hops.hopsworks.exceptions.InvalidQueryException;
import io.hops.hopsworks.exceptions.MetadataException;
import io.hops.hopsworks.exceptions.ProvenanceException;
+import io.hops.hopsworks.persistence.InvalidQueryException;
import io.hops.hopsworks.persistence.entity.dataset.Dataset;
import io.hops.hopsworks.persistence.entity.hdfs.inode.Inode;
import io.hops.hopsworks.persistence.entity.hdfs.user.HdfsUsers;
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/FeaturestoreKeywordResource.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/FeaturestoreKeywordResource.java
index 6487ffee1d..7111dfb6ae 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/FeaturestoreKeywordResource.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/FeaturestoreKeywordResource.java
@@ -19,7 +19,7 @@
import io.hops.hopsworks.common.featurestore.featureview.FeatureViewController;
import io.hops.hopsworks.api.filter.AllowedProjectRoles;
import io.hops.hopsworks.api.filter.Audience;
-import io.hops.hopsworks.api.filter.apiKey.ApiKeyRequired;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.common.api.ResourceRequest;
import io.hops.hopsworks.common.featurestore.featuregroup.FeaturegroupController;
import io.hops.hopsworks.common.featurestore.keyword.KeywordDTO;
@@ -127,7 +127,7 @@ public Response getKeywords(@Context SecurityContext sc,
}
return Response.ok().entity(dto).build();
}
-
+
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/FeaturestoreService.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/FeaturestoreService.java
index 9835fb4dc9..1c4cc61ea5 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/FeaturestoreService.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/FeaturestoreService.java
@@ -27,7 +27,7 @@
import io.hops.hopsworks.api.filter.AllowedProjectRoles;
import io.hops.hopsworks.api.filter.Audience;
import io.hops.hopsworks.api.filter.NoCacheResponse;
-import io.hops.hopsworks.api.filter.apiKey.ApiKeyRequired;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.common.api.ResourceRequest;
import io.hops.hopsworks.common.dao.project.ProjectFacade;
import io.hops.hopsworks.common.featurestore.FeaturestoreController;
@@ -111,7 +111,7 @@ public class FeaturestoreService {
public void setProjectId(Integer projectId) {
this.project = projectFacade.find(projectId);
}
-
+
/**
* Endpoint for getting the list of featurestores for the project
*
@@ -150,7 +150,7 @@ public Response getFeaturestores(
new GenericEntity>(featurestores) {};
return noCacheResponse.getNoCacheResponseBuilder(Response.Status.OK).entity(featurestoresGeneric).build();
}
-
+
/**
* Endpoint for getting a featurestore with a particular Id
*
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/FsQueryConstructorResource.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/FsQueryConstructorResource.java
index 0ff596be29..3196667dd6 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/FsQueryConstructorResource.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/FsQueryConstructorResource.java
@@ -18,7 +18,7 @@
import io.hops.hopsworks.api.filter.AllowedProjectRoles;
import io.hops.hopsworks.api.filter.Audience;
-import io.hops.hopsworks.api.filter.apiKey.ApiKeyRequired;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.api.jwt.JWTHelper;
import io.hops.hopsworks.common.featurestore.query.FsQueryDTO;
import io.hops.hopsworks.common.featurestore.query.QueryDTO;
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/activities/ActivityResource.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/activities/ActivityResource.java
index 9eaffe0768..22c63a8afd 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/activities/ActivityResource.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/activities/ActivityResource.java
@@ -19,7 +19,7 @@
import io.hops.hopsworks.common.featurestore.featureview.FeatureViewController;
import io.hops.hopsworks.api.filter.AllowedProjectRoles;
import io.hops.hopsworks.api.filter.Audience;
-import io.hops.hopsworks.api.filter.apiKey.ApiKeyRequired;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.api.jwt.JWTHelper;
import io.hops.hopsworks.api.util.Pagination;
import io.hops.hopsworks.common.api.ResourceRequest;
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/code/CodeResource.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/code/CodeResource.java
index 059c88c7c9..7df12f43f3 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/code/CodeResource.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/code/CodeResource.java
@@ -18,7 +18,7 @@
import io.hops.hopsworks.api.filter.AllowedProjectRoles;
import io.hops.hopsworks.api.filter.Audience;
-import io.hops.hopsworks.api.filter.apiKey.ApiKeyRequired;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.api.jwt.JWTHelper;
import io.hops.hopsworks.api.util.Pagination;
import io.hops.hopsworks.common.api.ResourceRequest;
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/commit/CommitResource.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/commit/CommitResource.java
index 3e30e95bb4..a4da001b82 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/commit/CommitResource.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/commit/CommitResource.java
@@ -18,7 +18,7 @@
import io.hops.hopsworks.api.filter.AllowedProjectRoles;
import io.hops.hopsworks.api.filter.Audience;
-import io.hops.hopsworks.api.filter.apiKey.ApiKeyRequired;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.api.jwt.JWTHelper;
import io.hops.hopsworks.api.util.Pagination;
import io.hops.hopsworks.common.api.ResourceRequest;
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/datavalidation/alert/FeatureGroupAlertResource.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/datavalidation/alert/FeatureGroupAlertResource.java
index 2a64beefd2..db39e72e17 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/datavalidation/alert/FeatureGroupAlertResource.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/datavalidation/alert/FeatureGroupAlertResource.java
@@ -30,7 +30,7 @@
import io.hops.hopsworks.api.alert.AlertDTO;
import io.hops.hopsworks.api.filter.AllowedProjectRoles;
import io.hops.hopsworks.api.filter.Audience;
-import io.hops.hopsworks.api.filter.apiKey.ApiKeyRequired;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.api.project.alert.ProjectAlertsDTO;
import io.hops.hopsworks.api.util.Pagination;
import io.hops.hopsworks.common.alert.AlertController;
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/datavalidationv2/expectations/ExpectationBuilder.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/datavalidationv2/expectations/ExpectationBuilder.java
index 14203b101c..a8149368e1 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/datavalidationv2/expectations/ExpectationBuilder.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/datavalidationv2/expectations/ExpectationBuilder.java
@@ -17,7 +17,6 @@
package io.hops.hopsworks.api.featurestore.datavalidationv2.expectations;
import io.hops.hopsworks.common.api.ResourceRequest;
-import io.hops.hopsworks.common.dao.AbstractFacade.CollectionInfo;
import io.hops.hopsworks.common.featurestore.datavalidationv2.expectations.ExpectationController;
import io.hops.hopsworks.common.featurestore.datavalidationv2.expectations.ExpectationDTO;
import io.hops.hopsworks.exceptions.FeaturestoreException;
@@ -25,6 +24,7 @@
import io.hops.hopsworks.persistence.entity.featurestore.featuregroup.datavalidationv2.Expectation;
import io.hops.hopsworks.persistence.entity.featurestore.featuregroup.datavalidationv2.ExpectationSuite;
import io.hops.hopsworks.persistence.entity.project.Project;
+import io.hops.hopsworks.persistence.entity.util.AbstractFacade;
import javax.ejb.EJB;
import javax.ejb.Stateless;
@@ -70,7 +70,7 @@ public ExpectationDTO build(UriInfo uriInfo, Project project,
ExpectationDTO dtos = new ExpectationDTO();
uri(dtos, uriInfo, project, featuregroup, expectationSuite);
- CollectionInfo expectations =
+ AbstractFacade.CollectionInfo expectations =
expectationController.getExpectationsByExpectationSuite(expectationSuite);
ArrayList listOfExpectationDTO = new ArrayList<>();
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/datavalidationv2/expectations/ExpectationResource.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/datavalidationv2/expectations/ExpectationResource.java
index e97509d3b2..d991d591f0 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/datavalidationv2/expectations/ExpectationResource.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/datavalidationv2/expectations/ExpectationResource.java
@@ -18,7 +18,7 @@
import io.hops.hopsworks.api.filter.AllowedProjectRoles;
import io.hops.hopsworks.api.filter.Audience;
-import io.hops.hopsworks.api.filter.apiKey.ApiKeyRequired;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.api.jwt.JWTHelper;
import io.hops.hopsworks.common.featurestore.datavalidationv2.expectations.ExpectationController;
import io.hops.hopsworks.common.featurestore.datavalidationv2.expectations.ExpectationDTO;
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/datavalidationv2/greatexpectations/GreatExpectationBuilder.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/datavalidationv2/greatexpectations/GreatExpectationBuilder.java
index 6c3037b1dd..ca8fc5ccc0 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/datavalidationv2/greatexpectations/GreatExpectationBuilder.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/datavalidationv2/greatexpectations/GreatExpectationBuilder.java
@@ -17,11 +17,11 @@
package io.hops.hopsworks.api.featurestore.datavalidationv2.greatexpectations;
import io.hops.hopsworks.common.api.ResourceRequest;
-import io.hops.hopsworks.common.dao.AbstractFacade.CollectionInfo;
import io.hops.hopsworks.common.featurestore.datavalidationv2.suites.ExpectationSuiteController;
import io.hops.hopsworks.persistence.entity.featurestore.Featurestore;
import io.hops.hopsworks.persistence.entity.featurestore.featuregroup.datavalidationv2.GreatExpectation;
import io.hops.hopsworks.persistence.entity.project.Project;
+import io.hops.hopsworks.persistence.entity.util.AbstractFacade;
import javax.ejb.EJB;
import javax.ejb.Stateless;
@@ -59,7 +59,8 @@ public GreatExpectationDTO build(UriInfo uriInfo, Project project, Featurestore
GreatExpectationDTO dtos = new GreatExpectationDTO();
uri(dtos, uriInfo, project, featurestore);
- CollectionInfo greatExpectations = expectationSuiteController.getAllGreatExpectations();
+ AbstractFacade.CollectionInfo greatExpectations = expectationSuiteController
+ .getAllGreatExpectations();
dtos.setItems(greatExpectations.getItems().stream()
.map(greatExpectation -> build(
uriInfo, project, featurestore, greatExpectation))
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/datavalidationv2/greatexpectations/GreatExpectationResource.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/datavalidationv2/greatexpectations/GreatExpectationResource.java
index 180123c745..1518e086e8 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/datavalidationv2/greatexpectations/GreatExpectationResource.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/datavalidationv2/greatexpectations/GreatExpectationResource.java
@@ -18,7 +18,7 @@
import io.hops.hopsworks.api.filter.AllowedProjectRoles;
import io.hops.hopsworks.api.filter.Audience;
-import io.hops.hopsworks.api.filter.apiKey.ApiKeyRequired;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.jwt.annotation.JWTRequired;
import io.hops.hopsworks.persistence.entity.featurestore.Featurestore;
import io.hops.hopsworks.persistence.entity.project.Project;
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/datavalidationv2/reports/ValidationReportBuilder.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/datavalidationv2/reports/ValidationReportBuilder.java
index 7ee3324327..8402682162 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/datavalidationv2/reports/ValidationReportBuilder.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/datavalidationv2/reports/ValidationReportBuilder.java
@@ -18,7 +18,6 @@
import io.hops.hopsworks.api.featurestore.datavalidationv2.results.ValidationResultBuilder;
import io.hops.hopsworks.common.api.ResourceRequest;
-import io.hops.hopsworks.common.dao.AbstractFacade.CollectionInfo;
import io.hops.hopsworks.common.featurestore.datavalidationv2.reports.ValidationReportController;
import io.hops.hopsworks.common.featurestore.datavalidationv2.reports.ValidationReportDTO;
import io.hops.hopsworks.common.featurestore.datavalidationv2.results.ValidationResultDTO;
@@ -27,6 +26,7 @@
import io.hops.hopsworks.persistence.entity.featurestore.featuregroup.datavalidationv2.ValidationReport;
import io.hops.hopsworks.persistence.entity.featurestore.featuregroup.datavalidationv2.ValidationResult;
import io.hops.hopsworks.persistence.entity.project.Project;
+import io.hops.hopsworks.persistence.entity.util.AbstractFacade;
import org.apache.hadoop.fs.Path;
import javax.ejb.EJB;
@@ -97,7 +97,7 @@ public ValidationReportDTO build(UriInfo uriInfo, ResourceRequest resourceReques
ValidationReportDTO dtos = new ValidationReportDTO();
uri(dtos, uriInfo, project, featuregroup);
- CollectionInfo validationReports =
+ AbstractFacade.CollectionInfo validationReports =
validationReportController.getAllValidationReportByFeatureGroup(
resourceRequest.getOffset(), resourceRequest.getLimit(), resourceRequest.getSort(), resourceRequest.getFilter(),
featuregroup);
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/datavalidationv2/reports/ValidationReportResource.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/datavalidationv2/reports/ValidationReportResource.java
index f2c9eaca84..dd10cfddc6 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/datavalidationv2/reports/ValidationReportResource.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/datavalidationv2/reports/ValidationReportResource.java
@@ -20,7 +20,7 @@
import io.hops.hopsworks.common.featurestore.datavalidationv2.reports.ValidationReportDTO;
import io.hops.hopsworks.api.filter.AllowedProjectRoles;
import io.hops.hopsworks.api.filter.Audience;
-import io.hops.hopsworks.api.filter.apiKey.ApiKeyRequired;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.api.jwt.JWTHelper;
import io.hops.hopsworks.api.util.Pagination;
import io.hops.hopsworks.common.api.ResourceRequest;
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/datavalidationv2/results/ValidationResultBuilder.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/datavalidationv2/results/ValidationResultBuilder.java
index 8e858ad449..aac502fc21 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/datavalidationv2/results/ValidationResultBuilder.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/datavalidationv2/results/ValidationResultBuilder.java
@@ -17,7 +17,6 @@
package io.hops.hopsworks.api.featurestore.datavalidationv2.results;
import io.hops.hopsworks.common.api.ResourceRequest;
-import io.hops.hopsworks.common.dao.AbstractFacade.CollectionInfo;
import io.hops.hopsworks.common.featurestore.datavalidationv2.results.ValidationResultController;
import io.hops.hopsworks.common.featurestore.datavalidationv2.results.ValidationResultDTO;
import io.hops.hopsworks.persistence.entity.featurestore.featuregroup.Featuregroup;
@@ -33,6 +32,8 @@
import java.util.TimeZone;
import java.util.stream.Collectors;
import java.text.SimpleDateFormat;
+
+import io.hops.hopsworks.persistence.entity.util.AbstractFacade;
import org.json.JSONObject;
import org.json.JSONException;
@@ -94,7 +95,7 @@ public ValidationResultDTO buildHistory(UriInfo uriInfo, ResourceRequest resourc
ValidationResultDTO dtos = new ValidationResultDTO();
uri(dtos, uriInfo, project, featuregroup);
- CollectionInfo validationResults =
+ AbstractFacade.CollectionInfo validationResults =
validationResultController.getAllValidationResultByExpectationId(
resourceRequest.getOffset(), resourceRequest.getLimit(), resourceRequest.getSort(), resourceRequest.getFilter(),
expectationId);
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/datavalidationv2/results/ValidationResultResource.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/datavalidationv2/results/ValidationResultResource.java
index 6a6207f17e..eeea8f45c2 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/datavalidationv2/results/ValidationResultResource.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/datavalidationv2/results/ValidationResultResource.java
@@ -18,7 +18,7 @@
import io.hops.hopsworks.api.filter.AllowedProjectRoles;
import io.hops.hopsworks.api.filter.Audience;
-import io.hops.hopsworks.api.filter.apiKey.ApiKeyRequired;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.api.util.Pagination;
import io.hops.hopsworks.common.api.ResourceRequest;
import io.hops.hopsworks.common.featurestore.datavalidationv2.results.ValidationResultDTO;
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/datavalidationv2/suites/ExpectationSuiteResource.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/datavalidationv2/suites/ExpectationSuiteResource.java
index c6cf9f9a75..c4b58d715e 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/datavalidationv2/suites/ExpectationSuiteResource.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/datavalidationv2/suites/ExpectationSuiteResource.java
@@ -19,7 +19,7 @@
import io.hops.hopsworks.api.featurestore.datavalidationv2.expectations.ExpectationResource;
import io.hops.hopsworks.api.filter.AllowedProjectRoles;
import io.hops.hopsworks.api.filter.Audience;
-import io.hops.hopsworks.api.filter.apiKey.ApiKeyRequired;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.api.jobs.JobDTO;
import io.hops.hopsworks.api.jobs.JobsBuilder;
import io.hops.hopsworks.api.jwt.JWTHelper;
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/featuregroup/FeatureGroupPreviewResource.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/featuregroup/FeatureGroupPreviewResource.java
index 829f0b65ff..a29e43538c 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/featuregroup/FeatureGroupPreviewResource.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/featuregroup/FeatureGroupPreviewResource.java
@@ -19,7 +19,7 @@
import io.hops.hopsworks.api.filter.AllowedProjectRoles;
import io.hops.hopsworks.api.filter.Audience;
-import io.hops.hopsworks.api.filter.apiKey.ApiKeyRequired;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.api.jwt.JWTHelper;
import io.hops.hopsworks.common.featurestore.featuregroup.FeaturegroupController;
import io.hops.hopsworks.common.featurestore.featuregroup.cached.FeatureGroupStorage;
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/featuregroup/FeaturegroupService.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/featuregroup/FeaturegroupService.java
index 79bacf2da6..af5348ba97 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/featuregroup/FeaturegroupService.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/featuregroup/FeaturegroupService.java
@@ -35,7 +35,7 @@
import io.hops.hopsworks.api.filter.AllowedProjectRoles;
import io.hops.hopsworks.api.filter.Audience;
import io.hops.hopsworks.api.filter.NoCacheResponse;
-import io.hops.hopsworks.api.filter.apiKey.ApiKeyRequired;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.api.jwt.JWTHelper;
import io.hops.hopsworks.common.api.ResourceRequest;
import io.hops.hopsworks.common.featurestore.FeaturestoreController;
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/featureview/FeatureViewResource.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/featureview/FeatureViewResource.java
index 37fe6e061f..414eed6b2a 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/featureview/FeatureViewResource.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/featureview/FeatureViewResource.java
@@ -19,7 +19,7 @@
import com.google.api.client.util.Sets;
import io.hops.hopsworks.api.filter.AllowedProjectRoles;
import io.hops.hopsworks.api.filter.Audience;
-import io.hops.hopsworks.api.filter.apiKey.ApiKeyRequired;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.api.jwt.JWTHelper;
import io.hops.hopsworks.common.api.ResourceRequest;
import io.hops.hopsworks.common.dao.QueryParam;
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/preparestatement/PreparedStatementResource.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/preparestatement/PreparedStatementResource.java
index 55fc746ef5..385ede1b95 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/preparestatement/PreparedStatementResource.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/preparestatement/PreparedStatementResource.java
@@ -20,7 +20,7 @@
import io.hops.hopsworks.api.featurestore.trainingdataset.PreparedStatementBuilder;
import io.hops.hopsworks.api.filter.AllowedProjectRoles;
import io.hops.hopsworks.api.filter.Audience;
-import io.hops.hopsworks.api.filter.apiKey.ApiKeyRequired;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.api.jwt.JWTHelper;
import io.hops.hopsworks.common.api.ResourceRequest;
import io.hops.hopsworks.common.featurestore.query.ServingPreparedStatementDTO;
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/query/QueryResource.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/query/QueryResource.java
index f285f46a7c..fb509f26ae 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/query/QueryResource.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/query/QueryResource.java
@@ -20,7 +20,7 @@
import io.hops.hopsworks.common.featurestore.featureview.FeatureViewController;
import io.hops.hopsworks.api.filter.AllowedProjectRoles;
import io.hops.hopsworks.api.filter.Audience;
-import io.hops.hopsworks.api.filter.apiKey.ApiKeyRequired;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.api.jwt.JWTHelper;
import io.hops.hopsworks.common.featurestore.query.FsQueryDTO;
import io.hops.hopsworks.common.featurestore.query.Query;
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/statistics/StatisticsResource.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/statistics/StatisticsResource.java
index 0ef27881cd..22342bc211 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/statistics/StatisticsResource.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/statistics/StatisticsResource.java
@@ -18,7 +18,7 @@
import io.hops.hopsworks.api.filter.AllowedProjectRoles;
import io.hops.hopsworks.api.filter.Audience;
-import io.hops.hopsworks.api.filter.apiKey.ApiKeyRequired;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.api.jobs.JobDTO;
import io.hops.hopsworks.api.jobs.JobsBuilder;
import io.hops.hopsworks.api.jwt.JWTHelper;
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/storageconnector/FeaturestoreStorageConnectorService.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/storageconnector/FeaturestoreStorageConnectorService.java
index d844dd2876..2a832f837e 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/storageconnector/FeaturestoreStorageConnectorService.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/storageconnector/FeaturestoreStorageConnectorService.java
@@ -21,7 +21,7 @@
import io.hops.hopsworks.api.filter.AllowedProjectRoles;
import io.hops.hopsworks.api.filter.Audience;
import io.hops.hopsworks.api.filter.NoCacheResponse;
-import io.hops.hopsworks.api.filter.apiKey.ApiKeyRequired;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.api.jwt.JWTHelper;
import io.hops.hopsworks.common.featurestore.FeaturestoreController;
import io.hops.hopsworks.common.featurestore.FeaturestoreDTO;
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/tag/FeatureStoreTagResource.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/tag/FeatureStoreTagResource.java
index 3ab5cb72b9..5b0b9053bc 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/tag/FeatureStoreTagResource.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/tag/FeatureStoreTagResource.java
@@ -18,7 +18,7 @@
import io.hops.hopsworks.api.filter.AllowedProjectRoles;
import io.hops.hopsworks.api.filter.Audience;
-import io.hops.hopsworks.api.filter.apiKey.ApiKeyRequired;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.api.tags.TagsExpansionBeanParam;
import io.hops.hopsworks.common.api.ResourceRequest;
import io.hops.hopsworks.common.featurestore.metadata.FeatureStoreTagControllerIface;
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/trainingdataset/TrainingDatasetResource.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/trainingdataset/TrainingDatasetResource.java
index d548f620dd..7fe010f11d 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/trainingdataset/TrainingDatasetResource.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/trainingdataset/TrainingDatasetResource.java
@@ -21,7 +21,7 @@
import io.hops.hopsworks.api.featurestore.statistics.StatisticsResource;
import io.hops.hopsworks.api.filter.AllowedProjectRoles;
import io.hops.hopsworks.api.filter.Audience;
-import io.hops.hopsworks.api.filter.apiKey.ApiKeyRequired;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.api.jobs.JobDTO;
import io.hops.hopsworks.api.jobs.JobsBuilder;
import io.hops.hopsworks.api.jwt.JWTHelper;
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/trainingdataset/TrainingDatasetService.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/trainingdataset/TrainingDatasetService.java
index b52d8ee0b2..9cb64f1196 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/trainingdataset/TrainingDatasetService.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/trainingdataset/TrainingDatasetService.java
@@ -27,7 +27,7 @@
import io.hops.hopsworks.api.filter.AllowedProjectRoles;
import io.hops.hopsworks.api.filter.Audience;
import io.hops.hopsworks.api.filter.NoCacheResponse;
-import io.hops.hopsworks.api.filter.apiKey.ApiKeyRequired;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.api.jobs.JobDTO;
import io.hops.hopsworks.api.jobs.JobsBuilder;
import io.hops.hopsworks.api.jwt.JWTHelper;
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/transformation/TransformationResource.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/transformation/TransformationResource.java
index 441d90bae2..545dee6e5d 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/transformation/TransformationResource.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/transformation/TransformationResource.java
@@ -20,7 +20,7 @@
import io.hops.hopsworks.api.featurestore.transformationFunction.TransformationFunctionBuilder;
import io.hops.hopsworks.api.filter.AllowedProjectRoles;
import io.hops.hopsworks.api.filter.Audience;
-import io.hops.hopsworks.api.filter.apiKey.ApiKeyRequired;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.api.jwt.JWTHelper;
import io.hops.hopsworks.common.api.ResourceRequest;
import io.hops.hopsworks.common.featurestore.trainingdatasets.TrainingDatasetController;
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/transformationFunction/TransformationFunctionResource.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/transformationFunction/TransformationFunctionResource.java
index 70fd33ab82..21dc3b0ceb 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/transformationFunction/TransformationFunctionResource.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/transformationFunction/TransformationFunctionResource.java
@@ -18,7 +18,7 @@
import io.hops.hopsworks.api.filter.AllowedProjectRoles;
import io.hops.hopsworks.api.filter.Audience;
-import io.hops.hopsworks.api.filter.apiKey.ApiKeyRequired;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.api.jwt.JWTHelper;
import io.hops.hopsworks.api.util.Pagination;
import io.hops.hopsworks.common.api.ResourceRequest;
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/filter/ApiKeyFilter.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/filter/ApiKeyFilter.java
new file mode 100644
index 0000000000..379af95107
--- /dev/null
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/filter/ApiKeyFilter.java
@@ -0,0 +1,83 @@
+/*
+ * This file is part of Hopsworks
+ * Copyright (C) 2023, Hopsworks AB. All rights reserved
+ *
+ * Hopsworks is free software: you can redistribute it and/or modify it under the terms of
+ * the GNU Affero General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * Hopsworks 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License along with this program.
+ * If not, see .
+ */
+
+package io.hops.hopsworks.api.filter;
+
+import io.hops.hopsworks.api.auth.Configuration;
+import io.hops.hopsworks.api.auth.UserStatusValidator;
+import io.hops.hopsworks.api.auth.UserUtilities;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
+import io.hops.hopsworks.api.auth.key.ApiKeyUtilities;
+import io.hops.hopsworks.exceptions.ApiKeyException;
+import io.hops.hopsworks.exceptions.UserException;
+import io.hops.hopsworks.persistence.entity.user.Users;
+import io.hops.hopsworks.persistence.entity.user.security.apiKey.ApiKey;
+import io.hops.hopsworks.persistence.entity.user.security.apiKey.ApiScope;
+import io.hops.hopsworks.restutils.RESTLogLevel;
+
+import javax.annotation.Priority;
+import javax.ejb.EJB;
+import javax.ws.rs.Priorities;
+import javax.ws.rs.container.ResourceInfo;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.ext.Provider;
+import java.lang.reflect.Method;
+import java.util.List;
+import java.util.Set;
+
+@Provider
+@ApiKeyRequired
+@Priority(Priorities.AUTHENTICATION - 1)
+public class ApiKeyFilter extends io.hops.hopsworks.api.auth.key.ApiKeyFilter {
+ @EJB
+ private UserStatusValidator userStatusValidator;
+ @EJB
+ private ApiKeyUtilities apiKeyUtilities;
+ @EJB
+ private UserUtilities userUtilities;
+ @EJB
+ private Configuration conf;
+ @Context
+ private ResourceInfo resourceInfo;
+
+ protected void validateUserStatus(Users user) throws UserException {
+ userStatusValidator.checkStatus(user.getStatus());
+ }
+
+ protected ApiKey getApiKey(String key) throws ApiKeyException {
+ return apiKeyUtilities.getApiKey(key);
+ }
+
+ protected Set getApiScopes(ApiKey key) {
+ return apiKeyUtilities.getScopes(key);
+ }
+
+ protected List getUserRoles(Users user) {
+ return userUtilities.getUserRoles(user);
+ }
+
+ protected RESTLogLevel getRestLogLevel() {
+ return conf.getLogLevel(Configuration.AuthConfigurationKeys.HOPSWORKS_REST_LOG_LEVEL);
+ }
+
+ protected Class> getResourceClass() {
+ return resourceInfo.getResourceClass();
+ }
+
+ protected Method getResourceMethod() {
+ return resourceInfo.getResourceMethod();
+ }
+}
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/filter/AuthFilter.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/filter/AuthFilter.java
index 48c2917b46..1318f66890 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/filter/AuthFilter.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/filter/AuthFilter.java
@@ -17,10 +17,10 @@
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
-import io.hops.hopsworks.api.filter.apiKey.ApiKeyFilter;
-import io.hops.hopsworks.api.filter.apiKey.ApiKeyRequired;
-import io.hops.hopsworks.api.filter.util.HopsworksSecurityContext;
-import io.hops.hopsworks.api.filter.util.Subject;
+import io.hops.hopsworks.api.auth.HopsworksSecurityContext;
+import io.hops.hopsworks.api.auth.Subject;
+import io.hops.hopsworks.api.auth.key.ApiKeyFilter;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.api.util.RESTApiJsonResponse;
import io.hops.hopsworks.common.util.Settings;
import io.hops.hopsworks.jwt.AlgorithmFactory;
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/git/GitResource.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/git/GitResource.java
index e2da7c656a..24386ea930 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/git/GitResource.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/git/GitResource.java
@@ -18,7 +18,7 @@
import com.google.common.base.Strings;
import io.hops.hopsworks.api.filter.AllowedProjectRoles;
import io.hops.hopsworks.api.filter.Audience;
-import io.hops.hopsworks.api.filter.apiKey.ApiKeyRequired;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.api.git.branch.BranchBuilder;
import io.hops.hopsworks.api.git.branch.BranchDTO;
import io.hops.hopsworks.api.git.execution.ExecutionBeanParam;
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/git/execution/GitExecutionResource.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/git/execution/GitExecutionResource.java
index 5e85f56df9..f048ecceb1 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/git/execution/GitExecutionResource.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/git/execution/GitExecutionResource.java
@@ -17,7 +17,7 @@
import io.hops.hopsworks.api.filter.AllowedProjectRoles;
import io.hops.hopsworks.api.filter.Audience;
-import io.hops.hopsworks.api.filter.apiKey.ApiKeyRequired;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.api.jwt.JWTHelper;
import io.hops.hopsworks.api.util.Pagination;
import io.hops.hopsworks.common.api.ResourceRequest;
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/jobs/FlinkProxyServlet.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/jobs/FlinkProxyServlet.java
index 09438c21c9..c155f4da47 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/jobs/FlinkProxyServlet.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/jobs/FlinkProxyServlet.java
@@ -16,7 +16,8 @@
package io.hops.hopsworks.api.jobs;
import com.google.common.base.Strings;
-import io.hops.hopsworks.api.filter.apiKey.ApiKeyFilter;
+import io.hops.hopsworks.api.auth.key.ApiKeyFilter;
+import io.hops.hopsworks.api.auth.key.ApiKeyUtilities;
import io.hops.hopsworks.api.jwt.JWTHelper;
import io.hops.hopsworks.api.proxy.ProxyServlet;
import io.hops.hopsworks.common.dao.hdfsUser.HdfsUsersFacade;
@@ -25,7 +26,6 @@
import io.hops.hopsworks.common.dao.project.team.ProjectTeamFacade;
import io.hops.hopsworks.common.dao.user.UserFacade;
import io.hops.hopsworks.common.jobs.flink.FlinkMasterAddrCache;
-import io.hops.hopsworks.common.user.security.apiKey.ApiKeyController;
import io.hops.hopsworks.exceptions.ApiKeyException;
import io.hops.hopsworks.persistence.entity.hdfs.user.HdfsUsers;
import io.hops.hopsworks.persistence.entity.jobs.history.YarnApplicationstate;
@@ -62,7 +62,7 @@ public class FlinkProxyServlet extends ProxyServlet {
@EJB
private UserFacade userFacade;
@EJB
- private ApiKeyController apiKeyController;
+ private ApiKeyUtilities apiKeyUtilities;
@EJB
private JWTHelper jwtHelper;
@@ -84,7 +84,7 @@ protected void service(HttpServletRequest servletRequest, HttpServletResponse se
if (authorizationHeader.startsWith(ApiKeyFilter.API_KEY)){
try {
String key = authorizationHeader.substring(ApiKeyFilter.API_KEY.length()).trim();
- ApiKey apiKey = apiKeyController.getApiKey(key);
+ ApiKey apiKey = apiKeyUtilities.getApiKey(key);
user = apiKey.getUser();
} catch (ApiKeyException e) {
servletResponse.sendError(401, "Could not validate API key");
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/jobs/JobsResource.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/jobs/JobsResource.java
index 843406e65d..0fb488cf68 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/jobs/JobsResource.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/jobs/JobsResource.java
@@ -19,7 +19,7 @@
import com.google.common.base.Strings;
import io.hops.hopsworks.api.filter.AllowedProjectRoles;
import io.hops.hopsworks.api.filter.Audience;
-import io.hops.hopsworks.api.filter.apiKey.ApiKeyRequired;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.api.jobs.alert.JobAlertsResource;
import io.hops.hopsworks.api.jobs.executions.ExecutionsResource;
import io.hops.hopsworks.api.jobs.scheduler.JobScheduleV2Resource;
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/jobs/alert/JobAlertsResource.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/jobs/alert/JobAlertsResource.java
index 05020ab2ae..a397a3e998 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/jobs/alert/JobAlertsResource.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/jobs/alert/JobAlertsResource.java
@@ -31,7 +31,7 @@
import io.hops.hopsworks.api.alert.AlertDTO;
import io.hops.hopsworks.api.filter.AllowedProjectRoles;
import io.hops.hopsworks.api.filter.Audience;
-import io.hops.hopsworks.api.filter.apiKey.ApiKeyRequired;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.api.project.alert.ProjectAlertsDTO;
import io.hops.hopsworks.api.util.Pagination;
import io.hops.hopsworks.common.alert.AlertController;
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/jobs/executions/ExecutionsResource.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/jobs/executions/ExecutionsResource.java
index b66fec7a88..9adf5f9f3f 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/jobs/executions/ExecutionsResource.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/jobs/executions/ExecutionsResource.java
@@ -19,7 +19,7 @@
import com.google.common.base.Strings;
import io.hops.hopsworks.api.filter.AllowedProjectRoles;
import io.hops.hopsworks.api.filter.Audience;
-import io.hops.hopsworks.api.filter.apiKey.ApiKeyRequired;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.api.jwt.JWTHelper;
import io.hops.hopsworks.api.util.Pagination;
import io.hops.hopsworks.common.api.ResourceRequest;
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/jobs/scheduler/JobScheduleV2Resource.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/jobs/scheduler/JobScheduleV2Resource.java
index 51f734bde4..5081461a74 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/jobs/scheduler/JobScheduleV2Resource.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/jobs/scheduler/JobScheduleV2Resource.java
@@ -18,7 +18,7 @@
import io.hops.hopsworks.api.filter.AllowedProjectRoles;
import io.hops.hopsworks.api.filter.Audience;
-import io.hops.hopsworks.api.filter.apiKey.ApiKeyRequired;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.common.jobs.scheduler.JobScheduleV2Controller;
import io.hops.hopsworks.common.jobs.scheduler.JobScheduleV2DTO;
import io.hops.hopsworks.exceptions.JobException;
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/jwt/JWTHelper.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/jwt/JWTHelper.java
index 4e5f2e7597..b46ddd3797 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/jwt/JWTHelper.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/jwt/JWTHelper.java
@@ -17,15 +17,13 @@
import com.auth0.jwt.interfaces.DecodedJWT;
import com.google.common.base.Strings;
+import io.hops.hopsworks.api.auth.UserUtilities;
import io.hops.hopsworks.api.filter.Audience;
-import io.hops.hopsworks.api.user.ServiceJWTDTO;
import io.hops.hopsworks.common.dao.project.ProjectFacade;
import io.hops.hopsworks.common.dao.project.team.ProjectTeamFacade;
import io.hops.hopsworks.common.dao.user.BbcGroupFacade;
import io.hops.hopsworks.common.dao.user.UserFacade;
import io.hops.hopsworks.common.opensearch.OpenSearchJWTController;
-import io.hops.hopsworks.common.user.UsersController;
-import io.hops.hopsworks.common.util.DateUtils;
import io.hops.hopsworks.common.util.Settings;
import io.hops.hopsworks.exceptions.OpenSearchException;
import io.hops.hopsworks.jwt.Constants;
@@ -43,7 +41,6 @@
import io.hops.hopsworks.persistence.entity.user.BbcGroup;
import io.hops.hopsworks.persistence.entity.user.Users;
import io.hops.hopsworks.persistence.entity.user.security.ua.UserAccountStatus;
-import org.apache.commons.lang3.tuple.Pair;
import javax.ejb.EJB;
import javax.ejb.Stateless;
@@ -57,8 +54,6 @@
import javax.ws.rs.core.SecurityContext;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
-import java.time.LocalDateTime;
-import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -95,7 +90,7 @@ public class JWTHelper {
@EJB
private BbcGroupFacade bbcGroupFacade;
@EJB
- private UsersController userController;
+ private UserUtilities userUtilities;
@EJB
private Settings settings;
@EJB
@@ -250,7 +245,7 @@ public String createOneTimeToken(Users user, String[] roles, String issuer, Stri
public String createToken(Users user, String[] audience, String issuer, Date expiresAt, Map claims)
throws NoSuchAlgorithmException, SigningKeyNotFoundException, DuplicateSigningKeyException {
SignatureAlgorithm alg = SignatureAlgorithm.valueOf(settings.getJWTSignatureAlg());
- String[] roles = userController.getUserRoles(user).toArray(new String[0]);
+ String[] roles = userUtilities.getUserRoles(user).toArray(new String[0]);
claims = jwtController.addDefaultClaimsIfMissing(claims, true, settings.getJWTExpLeewaySec(), roles);
return jwtController.createToken(settings.getJWTSigningKeyName(), false, issuer, audience, expiresAt,
@@ -423,7 +418,7 @@ public String autoRenewToken(DecodedJWT decodedJWT)
if (!UserAccountStatus.ACTIVATED_ACCOUNT.equals(user.getStatus())) {
throw new NotRenewableException("User not active");
}
- List roles = userController.getUserRoles(user);
+ List roles = userUtilities.getUserRoles(user);
return jwtController.autoRenewToken(decodedJWT, roles.toArray(new String[0]));
}
@@ -450,56 +445,6 @@ public JWTResponseDTO renewToken(JsonWebTokenDTO jsonWebTokenDTO, boolean invali
return new JWTResponseDTO(token, newExp, nbf, expLeeway);
}
- /**
- * Helper method to generate one-time tokens for service JWT renewal and renew the
- * master service JWT
- * @param token2renew Service JWT to renew
- * @param oneTimeRenewalToken Valid one-time token associated with the master token to be renewed.
- * One time tokens are generated once a service is logged-in and every time
- * it renews its master token
- * @param user Logged in user
- * @param remoteHostname Hostname of the machine the service runs
- * @return Renewed master service JWT and five one-time tokens used to renew it
- * @throws JWTException
- * @throws NoSuchAlgorithmException
- */
- public ServiceJWTDTO renewServiceToken(JsonWebTokenDTO token2renew, String oneTimeRenewalToken,
- Users user, String remoteHostname) throws JWTException, NoSuchAlgorithmException {
- if (Strings.isNullOrEmpty(oneTimeRenewalToken)) {
- throw new VerificationException("Service renewal token cannot be null or empty");
- }
- if (user == null) {
- DecodedJWT decodedJWT = jwtController.decodeToken(oneTimeRenewalToken);
- throw new VerificationException("Could not find user associated with JWT with ID: " + decodedJWT.getId());
- }
-
- LocalDateTime now = DateUtils.getNow();
- Date expiresAt = token2renew.getExpiresAt() != null ? token2renew.getExpiresAt()
- : DateUtils.localDateTime2Date(now.plus(settings.getServiceJWTLifetimeMS(), ChronoUnit.MILLIS));
- Date notBefore = token2renew.getNbf() != null ? token2renew.getNbf()
- : DateUtils.localDateTime2Date(now);
-
- List userRoles = userController.getUserRoles(user);
- Pair renewedTokens = jwtController.renewServiceToken(oneTimeRenewalToken, token2renew.getToken(),
- expiresAt, notBefore, settings.getServiceJWTLifetimeMS(), user.getUsername(),
- userRoles, SERVICE_RENEW_JWT_AUDIENCE, remoteHostname, settings.getJWTIssuer(),
- settings.getJWTSigningKeyName(), false);
-
- int expLeeway = jwtController.getExpLeewayClaim(jwtController.decodeToken(renewedTokens.getLeft()));
- JWTResponseDTO renewedServiceToken = new JWTResponseDTO(renewedTokens.getLeft(), expiresAt, notBefore, expLeeway);
-
- return new ServiceJWTDTO(renewedServiceToken, renewedTokens.getRight());
- }
-
- /**
- * Invalidate a service master token and delete the signing key of the temporary
- * one-time tokens.
- * @param serviceToken2invalidate
- */
- public void invalidateServiceToken(String serviceToken2invalidate) {
- jwtController.invalidateServiceToken(serviceToken2invalidate, settings.getJWTSigningKeyName());
- }
-
/**
* Invalidate a jwt found in the request header Authorization field.
*
@@ -638,7 +583,7 @@ public String createTokenForProxy(Users user)
throws DuplicateSigningKeyException, SigningKeyNotFoundException, NoSuchAlgorithmException {
SignatureAlgorithm alg = SignatureAlgorithm.valueOf(settings.getJWTSignatureAlg());
Date expiresAt = new Date(System.currentTimeMillis() + settings.getJWTLifetimeMs());
- String[] userRoles = userController.getUserRoles(user).toArray(new String[0]);
+ String[] userRoles = userUtilities.getUserRoles(user).toArray(new String[0]);
String[] audience = new String[] {Audience.PROXY};
Map claims = new HashMap<>();
claims.put(ROLES, userRoles);
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/jwt/JWTResource.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/jwt/JWTResource.java
index fd5dddd224..6c9eeb07ce 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/jwt/JWTResource.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/jwt/JWTResource.java
@@ -15,21 +15,16 @@
*/
package io.hops.hopsworks.api.jwt;
-import com.auth0.jwt.JWT;
-import com.auth0.jwt.interfaces.DecodedJWT;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.api.filter.Audience;
-import io.hops.hopsworks.api.user.ServiceJWTDTO;
import io.hops.hopsworks.common.util.Settings;
import io.hops.hopsworks.exceptions.OpenSearchException;
-import io.hops.hopsworks.exceptions.HopsSecurityException;
import io.hops.hopsworks.jwt.annotation.JWTRequired;
import io.hops.hopsworks.jwt.exception.DuplicateSigningKeyException;
import io.hops.hopsworks.jwt.exception.InvalidationException;
-import io.hops.hopsworks.jwt.exception.JWTException;
import io.hops.hopsworks.jwt.exception.NotRenewableException;
import io.hops.hopsworks.jwt.exception.SigningKeyNotFoundException;
-import io.hops.hopsworks.persistence.entity.user.Users;
-import io.hops.hopsworks.restutils.RESTCodes;
+import io.hops.hopsworks.persistence.entity.user.security.apiKey.ApiScope;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
@@ -53,7 +48,6 @@
import javax.ws.rs.core.SecurityContext;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
-import java.util.logging.Level;
import java.util.logging.Logger;
@Path("/jwt")
@@ -74,7 +68,10 @@ public class JWTResource {
@POST
@ApiOperation(value = "Create application token", response = JWTResponseDTO.class)
- public Response createToken(JWTRequestDTO jWTRequestDTO, @Context SecurityContext sc) throws NoSuchAlgorithmException,
+ @ApiKeyRequired(acceptedScopes = {ApiScope.AUTH}, allowedUserRoles = {"HOPS_ADMIN", "AGENT"})
+ public Response createToken(JWTRequestDTO jWTRequestDTO,
+ @Context HttpServletRequest req,
+ @Context SecurityContext sc) throws NoSuchAlgorithmException,
SigningKeyNotFoundException, DuplicateSigningKeyException {
JWTResponseDTO jWTResponseDTO = jWTHelper.createToken(jWTRequestDTO, settings.getJWTIssuer());
return Response.ok().entity(jWTResponseDTO).build();
@@ -82,7 +79,10 @@ public Response createToken(JWTRequestDTO jWTRequestDTO, @Context SecurityContex
@PUT
@ApiOperation(value = "Renew application token", response = JWTResponseDTO.class)
- public Response renewToken(JsonWebTokenDTO jsonWebTokenDTO, @Context SecurityContext sc)
+ @ApiKeyRequired(acceptedScopes = {ApiScope.AUTH}, allowedUserRoles = {"HOPS_ADMIN", "AGENT"})
+ public Response renewToken(JsonWebTokenDTO jsonWebTokenDTO,
+ @Context HttpServletRequest req,
+ @Context SecurityContext sc)
throws SigningKeyNotFoundException, NotRenewableException, InvalidationException {
JWTResponseDTO jWTResponseDTO = jWTHelper.renewToken(jsonWebTokenDTO, true, new HashMap<>(3));
return Response.ok().entity(jWTResponseDTO).build();
@@ -91,9 +91,11 @@ public Response renewToken(JsonWebTokenDTO jsonWebTokenDTO, @Context SecurityCon
@DELETE
@Path("/{token}")
@ApiOperation(value = "Invalidate application token")
- public Response invalidateToken(
- @ApiParam(value = "Token to invalidate", required = true)
- @PathParam("token") String token, @Context SecurityContext sc) throws InvalidationException {
+ @ApiKeyRequired(acceptedScopes = {ApiScope.AUTH}, allowedUserRoles = {"HOPS_ADMIN", "AGENT"})
+ public Response invalidateToken(@ApiParam(value = "Token to invalidate", required = true)
+ @PathParam("token") String token,
+ @Context HttpServletRequest req,
+ @Context SecurityContext sc) throws InvalidationException {
jWTHelper.invalidateToken(token);
return Response.ok().build();
}
@@ -107,55 +109,6 @@ public Response removeSigingKey(
jWTHelper.deleteSigningKeyByName(keyName);
return Response.ok().build();
}
-
- @PUT
- @Path("/service")
- @ApiOperation(value = "Renew a service JWT without invalidating the previous token", response = ServiceJWTDTO.class)
- public Response renewServiceToken(JsonWebTokenDTO jwt, @Context HttpServletRequest request)
- throws HopsSecurityException {
- // This token should be the one-time renewal token
- String token = jWTHelper.getAuthToken(request);
- Users user = jWTHelper.getUserPrincipal(request);
- if (user == null) {
- DecodedJWT decodedJWT = JWT.decode(token);
- throw new HopsSecurityException(RESTCodes.SecurityErrorCode.NOT_RENEWABLE_TOKEN, Level.FINE,
- "User not found associated with that JWT", "Could not find user in the database associated with JWT "
- + decodedJWT.getId());
- }
- try {
- ServiceJWTDTO renewedTokens = jWTHelper.renewServiceToken(jwt, token, user, request.getRemoteHost());
- return Response.ok().entity(renewedTokens).build();
- } catch (JWTException | NoSuchAlgorithmException ex) {
- throw new HopsSecurityException(RESTCodes.SecurityErrorCode.NOT_RENEWABLE_TOKEN, Level.WARNING,
- "Could not renew service JWT", "Could not renew service JWT for " + request.getRemoteHost());
- }
- }
-
- @DELETE
- @Path("/service/{token}")
- @ApiOperation(value = "Invalidate a service JWT and also delete the signing key encoded in the token")
- public Response invalidateServiceToken(
- @ApiParam(value = "Service token to invalidate", required = true)
- @PathParam("token") String token, @Context HttpServletRequest request)
- throws HopsSecurityException {
- DecodedJWT jwt2invalidate = JWT.decode(token);
-
- Users user = jWTHelper.getUserPrincipal(request);
- if (user == null) {
- throw new HopsSecurityException(RESTCodes.SecurityErrorCode.INVALIDATION_ERROR, Level.FINE,
- "Could not find registered user", "Could not find registered user associated with JWT "
- + jwt2invalidate.getId());
- }
- if (!user.getUsername().equals(jwt2invalidate.getSubject())) {
- throw new HopsSecurityException(RESTCodes.SecurityErrorCode.INVALIDATION_ERROR, Level.FINE,
- "Tried to invalidate token with different subject",
- "User " + user.getUsername() + " tried to invalidate token with Subject " + jwt2invalidate.getSubject());
- }
-
- jWTHelper.invalidateServiceToken(token);
-
- return Response.ok().build();
- }
@GET
@Path("/elk/key")
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/kafka/KafkaResource.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/kafka/KafkaResource.java
index 4511d8d545..65c47f69b0 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/kafka/KafkaResource.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/kafka/KafkaResource.java
@@ -41,7 +41,7 @@
import io.hops.hopsworks.api.filter.AllowedProjectRoles;
import io.hops.hopsworks.api.filter.Audience;
-import io.hops.hopsworks.api.filter.apiKey.ApiKeyRequired;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.api.kafka.topics.TopicsBeanParam;
import io.hops.hopsworks.api.kafka.topics.TopicsBuilder;
import io.hops.hopsworks.api.util.Pagination;
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/modelregistry/ModelRegistryResource.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/modelregistry/ModelRegistryResource.java
index df6f63b969..5710b7b90a 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/modelregistry/ModelRegistryResource.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/modelregistry/ModelRegistryResource.java
@@ -18,7 +18,7 @@
import io.hops.hopsworks.api.filter.AllowedProjectRoles;
import io.hops.hopsworks.api.filter.Audience;
-import io.hops.hopsworks.api.filter.apiKey.ApiKeyRequired;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.api.filter.featureFlags.FeatureFlagRequired;
import io.hops.hopsworks.api.filter.featureFlags.FeatureFlags;
import io.hops.hopsworks.api.jwt.JWTHelper;
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/modelregistry/ModelRegistryTagResource.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/modelregistry/ModelRegistryTagResource.java
index 4caea68547..93b63c16ba 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/modelregistry/ModelRegistryTagResource.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/modelregistry/ModelRegistryTagResource.java
@@ -17,7 +17,7 @@
import io.hops.hopsworks.api.filter.AllowedProjectRoles;
import io.hops.hopsworks.api.filter.Audience;
-import io.hops.hopsworks.api.filter.apiKey.ApiKeyRequired;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.api.filter.featureFlags.FeatureFlagRequired;
import io.hops.hopsworks.api.filter.featureFlags.FeatureFlags;
import io.hops.hopsworks.api.jwt.JWTHelper;
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/modelregistry/models/ModelsResource.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/modelregistry/models/ModelsResource.java
index f94aa087f1..733d462f70 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/modelregistry/models/ModelsResource.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/modelregistry/models/ModelsResource.java
@@ -17,7 +17,7 @@
import io.hops.hopsworks.api.filter.AllowedProjectRoles;
import io.hops.hopsworks.api.filter.Audience;
-import io.hops.hopsworks.api.filter.apiKey.ApiKeyRequired;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.api.filter.featureFlags.FeatureFlagRequired;
import io.hops.hopsworks.api.filter.featureFlags.FeatureFlags;
import io.hops.hopsworks.api.jwt.JWTHelper;
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/opensearch/OpenSearchService.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/opensearch/OpenSearchService.java
index f35e72f333..c2b8b85a15 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/opensearch/OpenSearchService.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/opensearch/OpenSearchService.java
@@ -40,7 +40,7 @@
package io.hops.hopsworks.api.opensearch;
import com.google.common.base.Strings;
-import io.hops.hopsworks.api.filter.apiKey.ApiKeyRequired;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.api.opensearch.featurestore.OpenSearchFeaturestoreBuilder;
import io.hops.hopsworks.api.opensearch.featurestore.OpenSearchFeaturestoreDTO;
import io.hops.hopsworks.api.opensearch.featurestore.OpenSearchFeaturestoreRequest;
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/project/ProjectService.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/project/ProjectService.java
index de712e16f0..9dd66bc4ef 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/project/ProjectService.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/project/ProjectService.java
@@ -48,7 +48,7 @@
import io.hops.hopsworks.api.filter.AllowedProjectRoles;
import io.hops.hopsworks.api.filter.Audience;
import io.hops.hopsworks.api.filter.NoCacheResponse;
-import io.hops.hopsworks.api.filter.apiKey.ApiKeyRequired;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.api.git.GitResource;
import io.hops.hopsworks.api.jobs.JobsResource;
import io.hops.hopsworks.api.jupyter.JupyterService;
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/project/alert/ProjectAlertsResource.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/project/alert/ProjectAlertsResource.java
index 380cf159a8..dac16096bc 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/project/alert/ProjectAlertsResource.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/project/alert/ProjectAlertsResource.java
@@ -33,7 +33,7 @@
import io.hops.hopsworks.api.featurestore.datavalidation.alert.FeatureGroupAlertDTO;
import io.hops.hopsworks.api.filter.AllowedProjectRoles;
import io.hops.hopsworks.api.filter.Audience;
-import io.hops.hopsworks.api.filter.apiKey.ApiKeyRequired;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.api.jobs.alert.JobAlertsBuilder;
import io.hops.hopsworks.api.jobs.alert.JobAlertsDTO;
import io.hops.hopsworks.api.util.Pagination;
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/project/jobconfig/DefaultJobConfigurationResource.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/project/jobconfig/DefaultJobConfigurationResource.java
index cb8f738f39..708fd9350e 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/project/jobconfig/DefaultJobConfigurationResource.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/project/jobconfig/DefaultJobConfigurationResource.java
@@ -18,7 +18,7 @@
import io.hops.hopsworks.api.filter.AllowedProjectRoles;
import io.hops.hopsworks.api.filter.Audience;
-import io.hops.hopsworks.api.filter.apiKey.ApiKeyRequired;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.api.util.Pagination;
import io.hops.hopsworks.common.api.ResourceRequest;
import io.hops.hopsworks.common.dao.project.ProjectFacade;
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/provenance/ProjectProvenanceResource.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/provenance/ProjectProvenanceResource.java
index 57b784ea85..116e90bdd0 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/provenance/ProjectProvenanceResource.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/provenance/ProjectProvenanceResource.java
@@ -18,7 +18,7 @@
import io.hops.hopsworks.api.dataset.DatasetAccessType;
import io.hops.hopsworks.api.filter.AllowedProjectRoles;
import io.hops.hopsworks.api.filter.Audience;
-import io.hops.hopsworks.api.filter.apiKey.ApiKeyRequired;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.api.jwt.JWTHelper;
import io.hops.hopsworks.api.provenance.ops.ProvLinksBeanParams;
import io.hops.hopsworks.api.provenance.ops.ProvLinksBuilder;
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/provenance/ProvenanceResource.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/provenance/ProvenanceResource.java
index f40967b59d..e9898b2826 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/provenance/ProvenanceResource.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/provenance/ProvenanceResource.java
@@ -17,7 +17,7 @@
import io.hops.hopsworks.api.filter.AllowedProjectRoles;
import io.hops.hopsworks.api.filter.Audience;
-import io.hops.hopsworks.api.filter.apiKey.ApiKeyRequired;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.api.jwt.JWTHelper;
import io.hops.hopsworks.api.provenance.explicit.ExplicitProvenanceExpansionBeanParam;
import io.hops.hopsworks.api.provenance.explicit.ProvExplicitLinksBuilder;
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/python/conflicts/EnvironmentConflictsResource.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/python/conflicts/EnvironmentConflictsResource.java
index fa420f16c1..388fe81167 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/python/conflicts/EnvironmentConflictsResource.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/python/conflicts/EnvironmentConflictsResource.java
@@ -18,7 +18,7 @@
import com.logicalclocks.servicediscoverclient.exceptions.ServiceDiscoveryException;
import io.hops.hopsworks.api.filter.AllowedProjectRoles;
import io.hops.hopsworks.api.filter.Audience;
-import io.hops.hopsworks.api.filter.apiKey.ApiKeyRequired;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.common.api.ResourceRequest;
import io.hops.hopsworks.common.python.environment.EnvironmentController;
import io.hops.hopsworks.exceptions.PythonException;
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/python/environment/EnvironmentResource.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/python/environment/EnvironmentResource.java
index 1de934492d..713d400bcc 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/python/environment/EnvironmentResource.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/python/environment/EnvironmentResource.java
@@ -19,7 +19,7 @@
import com.logicalclocks.servicediscoverclient.exceptions.ServiceDiscoveryException;
import io.hops.hopsworks.api.filter.AllowedProjectRoles;
import io.hops.hopsworks.api.filter.Audience;
-import io.hops.hopsworks.api.filter.apiKey.ApiKeyRequired;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.api.jwt.JWTHelper;
import io.hops.hopsworks.api.project.util.DsPath;
import io.hops.hopsworks.api.project.util.PathValidator;
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/python/environment/command/EnvironmentCommandsResource.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/python/environment/command/EnvironmentCommandsResource.java
index 164b32eb34..26b1d47695 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/python/environment/command/EnvironmentCommandsResource.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/python/environment/command/EnvironmentCommandsResource.java
@@ -17,7 +17,7 @@
import io.hops.hopsworks.api.filter.AllowedProjectRoles;
import io.hops.hopsworks.api.filter.Audience;
-import io.hops.hopsworks.api.filter.apiKey.ApiKeyRequired;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.api.python.command.CommandBeanParam;
import io.hops.hopsworks.api.python.command.CommandBuilder;
import io.hops.hopsworks.api.python.command.CommandDTO;
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/python/environment/command/custom/EnvironmentCustomCommandsResource.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/python/environment/command/custom/EnvironmentCustomCommandsResource.java
index e0ac0343c8..db1dcbb79d 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/python/environment/command/custom/EnvironmentCustomCommandsResource.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/python/environment/command/custom/EnvironmentCustomCommandsResource.java
@@ -17,7 +17,7 @@
import io.hops.hopsworks.api.filter.AllowedProjectRoles;
import io.hops.hopsworks.api.filter.Audience;
-import io.hops.hopsworks.api.filter.apiKey.ApiKeyRequired;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.api.jwt.JWTHelper;
import io.hops.hopsworks.api.python.command.CommandBuilder;
import io.hops.hopsworks.api.python.command.CommandDTO;
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/python/environment/history/EnvironmentHistoryResource.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/python/environment/history/EnvironmentHistoryResource.java
index 82b82226f2..7f9007b98e 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/python/environment/history/EnvironmentHistoryResource.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/python/environment/history/EnvironmentHistoryResource.java
@@ -17,7 +17,7 @@
import io.hops.hopsworks.api.filter.AllowedProjectRoles;
import io.hops.hopsworks.api.filter.Audience;
-import io.hops.hopsworks.api.filter.apiKey.ApiKeyRequired;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.api.jwt.JWTHelper;
import io.hops.hopsworks.api.util.Pagination;
import io.hops.hopsworks.common.api.ResourceRequest;
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/python/library/LibraryResource.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/python/library/LibraryResource.java
index e5e79fa57d..53c12f8961 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/python/library/LibraryResource.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/python/library/LibraryResource.java
@@ -18,7 +18,7 @@
import com.google.common.base.Strings;
import io.hops.hopsworks.api.filter.AllowedProjectRoles;
import io.hops.hopsworks.api.filter.Audience;
-import io.hops.hopsworks.api.filter.apiKey.ApiKeyRequired;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.api.jwt.JWTHelper;
import io.hops.hopsworks.api.python.library.command.LibraryCommandsResource;
import io.hops.hopsworks.api.python.library.search.LibrarySearchBuilder;
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/python/library/command/LibraryCommandsResource.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/python/library/command/LibraryCommandsResource.java
index 141c3a8488..27fc1f72a1 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/python/library/command/LibraryCommandsResource.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/python/library/command/LibraryCommandsResource.java
@@ -17,7 +17,7 @@
import io.hops.hopsworks.api.filter.AllowedProjectRoles;
import io.hops.hopsworks.api.filter.Audience;
-import io.hops.hopsworks.api.filter.apiKey.ApiKeyRequired;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.api.python.command.CommandBeanParam;
import io.hops.hopsworks.api.python.command.CommandBuilder;
import io.hops.hopsworks.api.python.command.CommandDTO;
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/serving/ServingService.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/serving/ServingService.java
index ab97b2c158..b57f0b0b1b 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/serving/ServingService.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/serving/ServingService.java
@@ -18,7 +18,7 @@
import com.google.common.base.Strings;
import io.hops.hopsworks.api.filter.AllowedProjectRoles;
-import io.hops.hopsworks.api.filter.apiKey.ApiKeyRequired;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.api.filter.Audience;
import io.hops.hopsworks.api.filter.NoCacheResponse;
import io.hops.hopsworks.api.filter.featureFlags.FeatureFlagRequired;
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/serving/inference/InferenceResource.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/serving/inference/InferenceResource.java
index 0619c67fe2..f35f2892f7 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/serving/inference/InferenceResource.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/serving/inference/InferenceResource.java
@@ -19,8 +19,7 @@
import com.google.common.base.Strings;
import io.hops.hopsworks.api.filter.AllowedProjectRoles;
import io.hops.hopsworks.api.filter.Audience;
-import io.hops.hopsworks.api.filter.apiKey.ApiKeyRequired;
-import io.hops.hopsworks.api.jwt.JWTHelper;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.api.filter.featureFlags.FeatureFlagRequired;
import io.hops.hopsworks.api.filter.featureFlags.FeatureFlags;
import io.hops.hopsworks.common.dao.project.ProjectFacade;
@@ -32,7 +31,6 @@
import io.hops.hopsworks.exceptions.ServingException;
import io.hops.hopsworks.jwt.annotation.JWTRequired;
import io.hops.hopsworks.persistence.entity.project.Project;
-import io.hops.hopsworks.persistence.entity.user.Users;
import io.hops.hopsworks.persistence.entity.user.security.apiKey.ApiScope;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
@@ -71,13 +69,11 @@ public class InferenceResource {
private InferenceController inferenceController;
@EJB
private ProjectFacade projectFacade;
- @EJB
- private JWTHelper jWTHelper;
-
+
private Project project;
private final static Logger logger = Logger.getLogger(InferenceResource.class.getName());
-
+
public void setProjectId(Integer projectId) {
this.project = projectFacade.find(projectId);
}
@@ -103,7 +99,6 @@ public Response infer(
if (!Strings.isNullOrEmpty(modelVersion)) {
version = Integer.valueOf(modelVersion.split("/")[2]);
}
- Users user = jWTHelper.getUserPrincipal(sc);
String authHeader = httpHeaders.getRequestHeader(HttpHeaders.AUTHORIZATION).get(0);
String inferenceResult = inferenceController.infer(project, sc.getUserPrincipal().getName(), modelName, version,
verb, inferenceRequestJson, authHeader);
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/tags/TagSchemasResource.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/tags/TagSchemasResource.java
index 692c0cf824..30cda387f4 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/tags/TagSchemasResource.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/tags/TagSchemasResource.java
@@ -16,7 +16,7 @@
package io.hops.hopsworks.api.tags;
import io.hops.hopsworks.api.filter.Audience;
-import io.hops.hopsworks.api.filter.apiKey.ApiKeyRequired;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.api.util.Pagination;
import io.hops.hopsworks.common.api.ResourceRequest;
import io.hops.hopsworks.common.tags.SchemaDTO;
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/user/AuthService.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/user/AuthService.java
index 15db60d95c..09c1b1743c 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/user/AuthService.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/user/AuthService.java
@@ -39,6 +39,8 @@
package io.hops.hopsworks.api.user;
import com.google.common.base.Strings;
+import io.hops.hopsworks.api.auth.UserStatusValidator;
+import io.hops.hopsworks.api.auth.UserUtilities;
import io.hops.hopsworks.api.filter.Audience;
import io.hops.hopsworks.api.filter.JWTNotRequired;
import io.hops.hopsworks.api.filter.NoCacheResponse;
@@ -49,7 +51,6 @@
import io.hops.hopsworks.common.dao.user.UserFacade;
import io.hops.hopsworks.common.user.AuthController;
import io.hops.hopsworks.common.user.QrCode;
-import io.hops.hopsworks.common.user.UserStatusValidator;
import io.hops.hopsworks.common.user.UsersController;
import io.hops.hopsworks.common.util.DateUtils;
import io.hops.hopsworks.common.util.Settings;
@@ -79,7 +80,6 @@
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.Consumes;
import javax.ws.rs.CookieParam;
-import javax.ws.rs.DELETE;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
@@ -129,7 +129,9 @@ public class AuthService {
private Settings settings;
@EJB
private JWTController jwtController;
-
+ @EJB
+ private UserUtilities userUtilities;
+
@GET
@Path("session")
@JWTRequired(acceptedTokens = {Audience.API}, allowedUserRoles = {"HOPS_ADMIN", "HOPS_USER", "HOPS_SERVICE_USER"})
@@ -139,7 +141,7 @@ public Response session(@Context HttpServletRequest req) {
json.setData(req.getRemoteUser());
return noCacheResponse.getNoCacheResponseBuilder(Response.Status.OK).entity(json).build();
}
-
+
@GET
@Path("jwt/session")
@JWTRequired(acceptedTokens = {Audience.API}, allowedUserRoles = {"HOPS_ADMIN", "HOPS_USER", "HOPS_SERVICE_USER"})
@@ -256,7 +258,7 @@ public Response serviceLogin(@FormParam("email") String email, @FormParam("passw
LocalDateTime masterExpiration = DateUtils.getNow().plus(settings.getServiceJWTLifetimeMS(), ChronoUnit.MILLIS);
LocalDateTime notBefore = jwtController.computeNotBefore4ServiceRenewalTokens(masterExpiration);
LocalDateTime expiresAt = notBefore.plus(settings.getServiceJWTLifetimeMS(), ChronoUnit.MILLIS);
- List userRoles = userController.getUserRoles(user);
+ List userRoles = userUtilities.getUserRoles(user);
JsonWebToken renewalJWTSpec = new JsonWebToken();
renewalJWTSpec.setSubject(user.getUsername());
@@ -291,19 +293,7 @@ public Response serviceLogin(@FormParam("email") String email, @FormParam("passw
throw ex;
}
}
-
- @DELETE
- @Path("/service")
- @Produces(MediaType.APPLICATION_JSON)
- @JWTNotRequired
- public Response serviceLogout(@Context HttpServletRequest request) throws UserException, InvalidationException {
- if (jWTHelper.validToken(request, settings.getJWTIssuer())) {
- jwtController.invalidateServiceToken(jWTHelper.getAuthToken(request), settings.getJWTSigningKeyName());
- }
- logoutAndInvalidateSession(request, null);
- return Response.ok().build();
- }
-
+
@GET
@Path("isAdmin")
@Produces(MediaType.APPLICATION_JSON)
@@ -318,7 +308,7 @@ public Response isAdmin(@Context SecurityContext sc,
}
return Response.ok(json).build();
}
-
+
@POST
@Path("register")
@Produces(MediaType.APPLICATION_JSON)
@@ -451,7 +441,7 @@ private Response login(Users user, String password, HttpServletRequest req, Http
throw new UserException(RESTCodes.UserErrorCode.NO_ROLE_FOUND, Level.FINE,
null, RESTCodes.UserErrorCode.NO_ROLE_FOUND.getMessage());
}
-
+
statusValidator.checkStatus(user.getStatus());
try {
req.login(user.getEmail(), password);
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/user/UsersResource.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/user/UsersResource.java
index 0a9bd5a574..4ada4e1629 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/user/UsersResource.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/user/UsersResource.java
@@ -41,7 +41,7 @@
import io.hops.hopsworks.api.activities.UserActivitiesResource;
import io.hops.hopsworks.api.filter.Audience;
import io.hops.hopsworks.api.filter.JWTNotRequired;
-import io.hops.hopsworks.api.filter.apiKey.ApiKeyRequired;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.api.jwt.JWTHelper;
import io.hops.hopsworks.api.user.apiKey.ApiKeyResource;
import io.hops.hopsworks.api.util.Pagination;
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/user/apiKey/ApiKeyBuilder.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/user/apiKey/ApiKeyBuilder.java
index 5ee2d2495a..09bed19048 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/user/apiKey/ApiKeyBuilder.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/user/apiKey/ApiKeyBuilder.java
@@ -15,9 +15,9 @@
*/
package io.hops.hopsworks.api.user.apiKey;
+import io.hops.hopsworks.api.auth.key.ApiKeyFacade;
import io.hops.hopsworks.common.api.ResourceRequest;
import io.hops.hopsworks.common.dao.AbstractFacade;
-import io.hops.hopsworks.common.dao.user.security.apiKey.ApiKeyFacade;
import io.hops.hopsworks.exceptions.ApiKeyException;
import io.hops.hopsworks.persistence.entity.user.Users;
import io.hops.hopsworks.persistence.entity.user.security.apiKey.ApiKey;
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/user/apiKey/ApiKeyFilterBy.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/user/apiKey/ApiKeyFilterBy.java
index 3f58c23050..82dacdaebb 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/user/apiKey/ApiKeyFilterBy.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/user/apiKey/ApiKeyFilterBy.java
@@ -15,8 +15,8 @@
*/
package io.hops.hopsworks.api.user.apiKey;
+import io.hops.hopsworks.api.auth.key.ApiKeyFacade;
import io.hops.hopsworks.common.dao.AbstractFacade;
-import io.hops.hopsworks.common.dao.user.security.apiKey.ApiKeyFacade;
public class ApiKeyFilterBy implements AbstractFacade.FilterBy {
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/user/apiKey/ApiKeyResource.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/user/apiKey/ApiKeyResource.java
index 39d9d880fd..95b3329e2d 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/user/apiKey/ApiKeyResource.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/user/apiKey/ApiKeyResource.java
@@ -15,9 +15,10 @@
*/
package io.hops.hopsworks.api.user.apiKey;
+import io.hops.hopsworks.api.auth.key.ApiKeyUtilities;
import io.hops.hopsworks.api.filter.AllowedProjectRoles;
import io.hops.hopsworks.api.filter.Audience;
-import io.hops.hopsworks.api.filter.apiKey.ApiKeyRequired;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.api.jwt.JWTHelper;
import io.hops.hopsworks.api.util.Pagination;
import io.hops.hopsworks.common.api.ResourceRequest;
@@ -64,9 +65,9 @@
@RequestScoped
@TransactionAttribute(TransactionAttributeType.NEVER)
public class ApiKeyResource {
-
+
private static final Logger LOGGER = Logger.getLogger(ApiKeyResource.class.getName());
-
+
@EJB
private ApiKeyController apikeyController;
@EJB
@@ -75,7 +76,9 @@ public class ApiKeyResource {
private JWTHelper jwtHelper;
@EJB
private BbcGroupFacade bbcGroupFacade;
-
+ @EJB
+ private ApiKeyUtilities apiKeyUtilities;
+
@GET
@Produces(MediaType.APPLICATION_JSON)
@ApiOperation(value = "Get all api keys.", response = ApiKeyDTO.class)
@@ -93,7 +96,7 @@ public Response get(@BeanParam Pagination pagination,
ApiKeyDTO dto = apikeyBuilder.buildItems(uriInfo, resourceRequest, user);
return Response.ok().entity(dto).build();
}
-
+
@GET
@Path("{name}")
@Produces(MediaType.APPLICATION_JSON)
@@ -108,7 +111,7 @@ public Response getByName(@PathParam("name") String name,
ApiKeyDTO dto = apikeyBuilder.build(uriInfo, resourceRequest, user, name);
return Response.ok().entity(dto).build();
}
-
+
@GET
@Path("key")
@Produces(MediaType.APPLICATION_JSON)
@@ -119,11 +122,11 @@ public Response getByKey(@QueryParam("key") String key,
@Context HttpServletRequest req,
@Context SecurityContext sc) throws ApiKeyException {
ResourceRequest resourceRequest = new ResourceRequest(ResourceRequest.Name.APIKEY);
- ApiKey apikey = apikeyController.getApiKey(key);
+ ApiKey apikey = apiKeyUtilities.getApiKey(key);
ApiKeyDTO dto = apikeyBuilder.build(uriInfo, resourceRequest, apikey);
return Response.ok().entity(dto).build();
}
-
+
@PUT
@Produces(MediaType.APPLICATION_JSON)
@ApiOperation(value = "Update an api key.", response = ApiKeyDTO.class)
@@ -153,7 +156,7 @@ public Response update(@QueryParam("name") String name, @QueryParam("action") Ap
ApiKeyDTO dto = apikeyBuilder.build(uriInfo, resourceRequest, apikey);
return Response.ok().entity(dto).build();
}
-
+
@POST
@Produces(MediaType.APPLICATION_JSON)
@ApiOperation(value = "Create an api key.", response = ApiKeyDTO.class)
@@ -170,7 +173,7 @@ public Response create(@QueryParam("name") String name, @QueryParam("scope") Set
dto.setKey(apiKey);
return Response.created(dto.getHref()).entity(dto).build();
}
-
+
@DELETE
@Path("{name}")
@ApiOperation(value = "Delete api key by name.")
@@ -183,7 +186,7 @@ public Response deleteByName(@PathParam("name") String name, @Context UriInfo ur
apikeyController.deleteKey(user, name);
return Response.noContent().build();
}
-
+
@DELETE
@ApiOperation(value = "Delete all api keys.")
@AllowedProjectRoles({AllowedProjectRoles.DATA_OWNER, AllowedProjectRoles.DATA_SCIENTIST})
@@ -194,7 +197,7 @@ public Response deleteAll(@Context UriInfo uriInfo, @Context SecurityContext sc,
apikeyController.deleteAll(user);
return Response.noContent().build();
}
-
+
@GET
@Path("scopes")
@Produces(MediaType.APPLICATION_JSON)
@@ -211,7 +214,7 @@ public Response getScopes(@Context SecurityContext sc,
};
return Response.ok().entity(scopeEntity).build();
}
-
+
@GET
@Path("session")
@Produces(MediaType.APPLICATION_JSON)
@@ -257,6 +260,11 @@ private Set getScopesForUser(Users user) {
if (user.getBbcGroupCollection().size() == 1 && user.getBbcGroupCollection().contains(serviceGroup)) {
return ApiScope.getServiceUserScopes();
}
+
+ BbcGroup agentGroup = bbcGroupFacade.findByGroupName("AGENT");
+ if (user.getBbcGroupCollection().contains(agentGroup)) {
+ return ApiScope.getAgentUserScopes();
+ }
return ApiScope.getUnprivileged();
}
}
\ No newline at end of file
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/user/apiKey/ApiKeySortBy.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/user/apiKey/ApiKeySortBy.java
index d43e5aa194..a8a4e3a04c 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/user/apiKey/ApiKeySortBy.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/user/apiKey/ApiKeySortBy.java
@@ -15,8 +15,8 @@
*/
package io.hops.hopsworks.api.user.apiKey;
+import io.hops.hopsworks.api.auth.key.ApiKeyFacade;
import io.hops.hopsworks.common.dao.AbstractFacade;
-import io.hops.hopsworks.common.dao.user.security.apiKey.ApiKeyFacade;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/util/ClusterUtilisationService.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/util/ClusterUtilisationService.java
index 9ecc87a6e6..c3333e90f2 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/util/ClusterUtilisationService.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/util/ClusterUtilisationService.java
@@ -42,7 +42,7 @@
import com.logicalclocks.servicediscoverclient.exceptions.ServiceDiscoveryException;
import com.logicalclocks.servicediscoverclient.service.Service;
import io.hops.hopsworks.api.filter.Audience;
-import io.hops.hopsworks.api.filter.apiKey.ApiKeyRequired;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.common.dao.host.HostsFacade;
import io.hops.hopsworks.common.hosts.ServiceDiscoveryController;
import io.hops.hopsworks.common.proxies.client.HttpClient;
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/util/DownloadService.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/util/DownloadService.java
index 115cb2da9e..9934748d08 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/util/DownloadService.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/util/DownloadService.java
@@ -41,7 +41,7 @@
import com.auth0.jwt.interfaces.DecodedJWT;
import io.hops.hopsworks.api.filter.JWTNotRequired;
-import io.hops.hopsworks.api.filter.apiKey.ApiKeyRequired;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.common.dataset.util.DatasetHelper;
import io.hops.hopsworks.common.dataset.util.DatasetPath;
import io.hops.hopsworks.api.filter.AllowedProjectRoles;
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/util/UploadService.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/util/UploadService.java
index 45e059990f..4efe8314c6 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/util/UploadService.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/util/UploadService.java
@@ -40,7 +40,7 @@
import io.hops.hopsworks.api.filter.AllowedProjectRoles;
import io.hops.hopsworks.api.filter.Audience;
-import io.hops.hopsworks.api.filter.apiKey.ApiKeyRequired;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.api.jwt.JWTHelper;
import io.hops.hopsworks.common.dao.project.ProjectFacade;
import io.hops.hopsworks.common.dataset.DatasetController;
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/util/VariablesService.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/util/VariablesService.java
index 0635b37455..1d97b06375 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/util/VariablesService.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/util/VariablesService.java
@@ -41,7 +41,7 @@
import io.hops.hopsworks.api.filter.Audience;
import io.hops.hopsworks.api.filter.JWTNotRequired;
import io.hops.hopsworks.api.filter.NoCacheResponse;
-import io.hops.hopsworks.api.filter.apiKey.ApiKeyRequired;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.common.dao.remote.oauth.OauthClientFacade;
import io.hops.hopsworks.common.dataset.FolderNameValidator;
import io.hops.hopsworks.common.remote.RemoteUserHelper;
diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/rest/application/config/ApplicationConfig.java b/hopsworks-api/src/main/java/io/hops/hopsworks/rest/application/config/ApplicationConfig.java
index cc2a896b34..84e5e0a427 100644
--- a/hopsworks-api/src/main/java/io/hops/hopsworks/rest/application/config/ApplicationConfig.java
+++ b/hopsworks-api/src/main/java/io/hops/hopsworks/rest/application/config/ApplicationConfig.java
@@ -59,7 +59,7 @@ public ApplicationConfig() {
register(io.hops.hopsworks.api.exception.mapper.RESTApiThrowableMapper.class);
register(io.hops.hopsworks.api.filter.ProjectAuthFilter.class);
register(io.hops.hopsworks.api.filter.AuthFilter.class);
- register(io.hops.hopsworks.api.filter.apiKey.ApiKeyFilter.class);
+ register(io.hops.hopsworks.api.filter.ApiKeyFilter.class);
register(io.hops.hopsworks.api.filter.JWTAutoRenewFilter.class);
register(io.hops.hopsworks.api.filter.featureFlags.FeatureFlagFilter.class);
register(io.hops.hopsworks.api.jwt.JWTResource.class);
diff --git a/hopsworks-ca/pom.xml b/hopsworks-ca/pom.xml
index f3b1d75e15..74be93189f 100644
--- a/hopsworks-ca/pom.xml
+++ b/hopsworks-ca/pom.xml
@@ -85,6 +85,11 @@
io.hops.hopsworks
hopsworks-service-discovery
+
+ io.hops.hopsworks
+ hopsworks-api-auth
+ ejb
+
commons-io
diff --git a/hopsworks-ca/src/main/java/io/hops/hopsworks/ca/api/ApplicationConfig.java b/hopsworks-ca/src/main/java/io/hops/hopsworks/ca/api/ApplicationConfig.java
index b7909d2db1..e006b0c8ba 100644
--- a/hopsworks-ca/src/main/java/io/hops/hopsworks/ca/api/ApplicationConfig.java
+++ b/hopsworks-ca/src/main/java/io/hops/hopsworks/ca/api/ApplicationConfig.java
@@ -40,6 +40,7 @@
package io.hops.hopsworks.ca.api;
import io.hops.hopsworks.ca.api.exception.mapper.CAThrowableMapper;
+import io.hops.hopsworks.ca.api.filter.CaApiKeyFilter;
import io.swagger.annotations.Api;
import org.glassfish.jersey.server.ResourceConfig;
@@ -61,6 +62,7 @@ public ApplicationConfig() {
register(org.glassfish.jersey.media.multipart.MultiPartFeature.class);
+ register(CaApiKeyFilter.class);
// JWT filters
register(io.hops.hopsworks.ca.api.filter.AuthFilter.class);
register(io.hops.hopsworks.ca.api.filter.JWTAutoRenewFilter.class);
diff --git a/hopsworks-ca/src/main/java/io/hops/hopsworks/ca/api/certificates/AppCertsResource.java b/hopsworks-ca/src/main/java/io/hops/hopsworks/ca/api/certificates/AppCertsResource.java
index 7e96d4fb07..cbe3bb57c5 100644
--- a/hopsworks-ca/src/main/java/io/hops/hopsworks/ca/api/certificates/AppCertsResource.java
+++ b/hopsworks-ca/src/main/java/io/hops/hopsworks/ca/api/certificates/AppCertsResource.java
@@ -40,6 +40,7 @@
package io.hops.hopsworks.ca.api.certificates;
import com.google.common.base.Strings;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.ca.api.filter.Audience;
import io.hops.hopsworks.ca.api.filter.NoCacheResponse;
import io.hops.hopsworks.ca.controllers.CAException;
@@ -47,6 +48,7 @@
import io.hops.hopsworks.ca.controllers.PKI;
import io.hops.hopsworks.ca.controllers.PKIUtils;
import io.hops.hopsworks.jwt.annotation.JWTRequired;
+import io.hops.hopsworks.persistence.entity.user.security.apiKey.ApiScope;
import io.hops.hopsworks.restutils.RESTCodes;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
@@ -92,6 +94,7 @@ public class AppCertsResource {
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@JWTRequired(acceptedTokens={Audience.SERVICES}, allowedUserRoles={"AGENT"})
+ @ApiKeyRequired(acceptedScopes = {ApiScope.AUTH}, allowedUserRoles = {"AGENT"})
public Response signCSR(CSRView csrView) throws CAException {
if (csrView == null || Strings.isNullOrEmpty(csrView.getCsr())) {
throw new IllegalArgumentException("Empty CSR");
@@ -114,6 +117,7 @@ public Response signCSR(CSRView csrView) throws CAException {
@ApiOperation(value = "Revoke App certificate")
@DELETE
@JWTRequired(acceptedTokens={Audience.SERVICES}, allowedUserRoles={"AGENT"})
+ @ApiKeyRequired(acceptedScopes = {ApiScope.AUTH}, allowedUserRoles = {"AGENT"})
public Response revokeCertificate(
@ApiParam(value = "Identifier of the Certificate to revoke", required = true) @QueryParam("certId") String certId)
throws CAException {
diff --git a/hopsworks-ca/src/main/java/io/hops/hopsworks/ca/api/certificates/CertificatesResource.java b/hopsworks-ca/src/main/java/io/hops/hopsworks/ca/api/certificates/CertificatesResource.java
index 5485a692b2..c0e5779bc6 100644
--- a/hopsworks-ca/src/main/java/io/hops/hopsworks/ca/api/certificates/CertificatesResource.java
+++ b/hopsworks-ca/src/main/java/io/hops/hopsworks/ca/api/certificates/CertificatesResource.java
@@ -39,6 +39,7 @@
package io.hops.hopsworks.ca.api.certificates;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.ca.api.filter.Audience;
import io.hops.hopsworks.ca.api.filter.NoCacheResponse;
import io.hops.hopsworks.ca.controllers.CAException;
@@ -46,6 +47,7 @@
import io.hops.hopsworks.ca.controllers.PKIUtils;
import io.hops.hopsworks.jwt.annotation.JWTRequired;
import io.hops.hopsworks.persistence.entity.pki.PKICertificate;
+import io.hops.hopsworks.persistence.entity.user.security.apiKey.ApiScope;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
@@ -117,6 +119,7 @@ public CRLResource getCrlResource() {
@Produces(MediaType.APPLICATION_JSON)
@ApiOperation(value = "Get x509 certificate in pem format", response = CSRView.class)
@JWTRequired(acceptedTokens={Audience.SERVICES, Audience.API}, allowedUserRoles={"HOPS_ADMIN"})
+ @ApiKeyRequired(acceptedScopes = {ApiScope.AUTH}, allowedUserRoles = {"HOPS_ADMIN", "AGENT"})
public Response getCertificate(
@ApiParam(value = "X.500 name of the Certificate", required = true)
@PathParam("name") String name,
diff --git a/hopsworks-ca/src/main/java/io/hops/hopsworks/ca/api/certificates/HostCertsResource.java b/hopsworks-ca/src/main/java/io/hops/hopsworks/ca/api/certificates/HostCertsResource.java
index e85d257ee4..35a0b39645 100644
--- a/hopsworks-ca/src/main/java/io/hops/hopsworks/ca/api/certificates/HostCertsResource.java
+++ b/hopsworks-ca/src/main/java/io/hops/hopsworks/ca/api/certificates/HostCertsResource.java
@@ -40,6 +40,7 @@
package io.hops.hopsworks.ca.api.certificates;
import com.google.common.base.Strings;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.ca.api.filter.Audience;
import io.hops.hopsworks.ca.api.filter.NoCacheResponse;
import io.hops.hopsworks.ca.controllers.CAException;
@@ -48,6 +49,7 @@
import io.hops.hopsworks.ca.controllers.PKI;
import io.hops.hopsworks.ca.controllers.PKIUtils;
import io.hops.hopsworks.jwt.annotation.JWTRequired;
+import io.hops.hopsworks.persistence.entity.user.security.apiKey.ApiScope;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
@@ -97,6 +99,7 @@ public HostCertsResource() { }
@Produces(MediaType.APPLICATION_JSON)
@ApiOperation(value = "Sing Host CSR with IntermediateHopsCA", response = CSRView.class)
@JWTRequired(acceptedTokens={Audience.SERVICES}, allowedUserRoles={"AGENT"})
+ @ApiKeyRequired(acceptedScopes = {ApiScope.AUTH}, allowedUserRoles = {"AGENT"})
public Response signCSR(CSRView csrView) throws CAException {
if (csrView == null || csrView.getCsr() == null || csrView.getCsr().isEmpty()) {
throw new IllegalArgumentException("Empty CSR");
@@ -124,6 +127,7 @@ public Response signCSR(CSRView csrView) throws CAException {
@DELETE
@ApiOperation(value = "Revoke Host certificate")
@JWTRequired(acceptedTokens={Audience.SERVICES}, allowedUserRoles={"AGENT"})
+ @ApiKeyRequired(acceptedScopes = {ApiScope.AUTH}, allowedUserRoles = {"AGENT"})
public Response revokeCertificate(
@ApiParam(value = "Identifier of the Certificate to revoke", required = true)
@QueryParam("certId") String certId,
@@ -160,6 +164,7 @@ public Response revokeCertificate(
@DELETE
@ApiOperation(value = "Revoke all Host certificates")
@JWTRequired(acceptedTokens={Audience.SERVICES}, allowedUserRoles={"AGENT"})
+ @ApiKeyRequired(acceptedScopes = {ApiScope.AUTH}, allowedUserRoles = {"AGENT"})
public Response revokeCertificateGlob(
@ApiParam(value = "Hostname of the node to revoke certificates for", required = true)
@QueryParam("hostname")
diff --git a/hopsworks-ca/src/main/java/io/hops/hopsworks/ca/api/certificates/KubeCertsResource.java b/hopsworks-ca/src/main/java/io/hops/hopsworks/ca/api/certificates/KubeCertsResource.java
index 30b2b03490..740429f945 100644
--- a/hopsworks-ca/src/main/java/io/hops/hopsworks/ca/api/certificates/KubeCertsResource.java
+++ b/hopsworks-ca/src/main/java/io/hops/hopsworks/ca/api/certificates/KubeCertsResource.java
@@ -39,18 +39,23 @@
package io.hops.hopsworks.ca.api.certificates;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
+import com.google.common.base.Strings;
import io.hops.hopsworks.ca.api.filter.Audience;
import io.hops.hopsworks.ca.api.filter.NoCacheResponse;
import io.hops.hopsworks.ca.controllers.CAException;
import io.hops.hopsworks.ca.controllers.CAInitializationException;
+import io.hops.hopsworks.ca.controllers.CertificateNotFoundException;
import io.hops.hopsworks.ca.controllers.PKI;
import io.hops.hopsworks.ca.controllers.PKIUtils;
import io.hops.hopsworks.jwt.annotation.JWTRequired;
+import io.hops.hopsworks.persistence.entity.user.security.apiKey.ApiScope;
import io.hops.hopsworks.restutils.RESTCodes;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.apache.commons.lang3.tuple.Pair;
+import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.operator.OperatorCreationException;
import javax.ejb.EJB;
@@ -68,6 +73,8 @@
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.List;
import java.util.logging.Level;
import static io.hops.hopsworks.ca.controllers.CertificateType.KUBE;
@@ -88,6 +95,7 @@ public class KubeCertsResource{
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@JWTRequired(acceptedTokens={Audience.SERVICES}, allowedUserRoles={"AGENT"})
+ @ApiKeyRequired(acceptedScopes = {ApiScope.AUTH}, allowedUserRoles = {"AGENT"})
public Response signCSR(CSRView csrView) throws CAException {
if (csrView == null || csrView.getCsr() == null || csrView.getCsr().isEmpty()) {
throw new IllegalArgumentException("Empty CSR");
@@ -109,15 +117,32 @@ public Response signCSR(CSRView csrView) throws CAException {
@ApiOperation(value = "Revoke KubeCA certificates")
@DELETE
@JWTRequired(acceptedTokens={Audience.SERVICES}, allowedUserRoles={"AGENT"})
+ @ApiKeyRequired(acceptedScopes = {ApiScope.AUTH}, allowedUserRoles = {"AGENT"})
public Response revokeCertificate(
- @ApiParam(value = "Identifier of the Certificate to revoke", required = true) @QueryParam("certId") String certId)
- throws CAException {
- if (certId == null || certId.isEmpty()) {
+ @ApiParam(value = "Identifier of the Certificate to revoke", required = true)
+ @QueryParam("certId") String certId,
+ @ApiParam(value = "Flag whether certId is a full RFC4514 Distinguished Name string")
+ @QueryParam("exact") Boolean exact) throws CAException {
+ if (Strings.isNullOrEmpty(certId)) {
throw new IllegalArgumentException("Empty certificate identifier");
}
+ final List subjectsToRevoke = new ArrayList<>();
try {
- pki.revokeCertificate(certId, KUBE);
+ if (!Boolean.TRUE.equals(exact)) {
+ X500Name certificateName = pkiUtils.parseCertificateSubjectName(certId, KUBE);
+ List subjects = pkiUtils.findAllValidSubjectsWithPartialMatch(certificateName.toString());
+ subjects.forEach(s -> subjectsToRevoke.add(new X500Name(s)));
+ } else {
+ subjectsToRevoke.add(new X500Name(certId));
+ }
+ if (subjectsToRevoke.isEmpty()) {
+ throw new CertificateNotFoundException("Could not find a VALID certificate with ID: " + certId + " Is " +
+ "exact X509 Name: " + exact);
+ }
+ for (X500Name n : subjectsToRevoke) {
+ pki.revokeCertificate(n, KUBE);
+ }
return Response.ok().build();
} catch (InvalidNameException | GeneralSecurityException | CAInitializationException ex) {
throw pkiUtils.certificateRevocationExceptionConvertToCAException(ex, KUBE);
@@ -128,6 +153,7 @@ public Response revokeCertificate(
@GET
@Produces(MediaType.APPLICATION_JSON)
@JWTRequired(acceptedTokens={Audience.SERVICES}, allowedUserRoles={"AGENT"})
+ @ApiKeyRequired(acceptedScopes = {ApiScope.AUTH}, allowedUserRoles = {"AGENT"})
public Response getCACert() throws CAException {
try {
Pair chainOfTrust = pki.getChainOfTrust(pkiUtils.getResponsibleCA(KUBE));
diff --git a/hopsworks-ca/src/main/java/io/hops/hopsworks/ca/api/certificates/PKIResource.java b/hopsworks-ca/src/main/java/io/hops/hopsworks/ca/api/certificates/PKIResource.java
index 489bf4536a..d9db5ab02a 100644
--- a/hopsworks-ca/src/main/java/io/hops/hopsworks/ca/api/certificates/PKIResource.java
+++ b/hopsworks-ca/src/main/java/io/hops/hopsworks/ca/api/certificates/PKIResource.java
@@ -15,10 +15,12 @@
*/
package io.hops.hopsworks.ca.api.certificates;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.ca.api.filter.Audience;
import io.hops.hopsworks.ca.api.filter.NoCacheResponse;
import io.hops.hopsworks.ca.controllers.PKI;
import io.hops.hopsworks.jwt.annotation.JWTRequired;
+import io.hops.hopsworks.persistence.entity.user.security.apiKey.ApiScope;
import javax.ejb.EJB;
import javax.ejb.TransactionAttribute;
@@ -44,6 +46,7 @@ public class PKIResource {
@PUT
@Path("/configuration")
+ @ApiKeyRequired(acceptedScopes = {ApiScope.AUTH}, allowedUserRoles = {"AGENT"})
public Response reconfigure() {
LOGGER.log(Level.INFO, "Loading PKI configuration");
pki.configure();
diff --git a/hopsworks-ca/src/main/java/io/hops/hopsworks/ca/api/certificates/ProjectCertsResource.java b/hopsworks-ca/src/main/java/io/hops/hopsworks/ca/api/certificates/ProjectCertsResource.java
index 364017ca5c..6c3fb59975 100644
--- a/hopsworks-ca/src/main/java/io/hops/hopsworks/ca/api/certificates/ProjectCertsResource.java
+++ b/hopsworks-ca/src/main/java/io/hops/hopsworks/ca/api/certificates/ProjectCertsResource.java
@@ -17,6 +17,7 @@
package io.hops.hopsworks.ca.api.certificates;
import com.google.common.base.Strings;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.ca.api.filter.Audience;
import io.hops.hopsworks.ca.api.filter.NoCacheResponse;
import io.hops.hopsworks.ca.controllers.CAException;
@@ -24,6 +25,7 @@
import io.hops.hopsworks.ca.controllers.PKI;
import io.hops.hopsworks.ca.controllers.PKIUtils;
import io.hops.hopsworks.jwt.annotation.JWTRequired;
+import io.hops.hopsworks.persistence.entity.user.security.apiKey.ApiScope;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
@@ -63,7 +65,8 @@ public class ProjectCertsResource {
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@JWTRequired(acceptedTokens={Audience.SERVICES}, allowedUserRoles={"AGENT"})
- public Response signCSR(CSRView csrView) throws IOException, CAException {
+ @ApiKeyRequired(acceptedScopes = {ApiScope.AUTH}, allowedUserRoles = {"AGENT"})
+ public Response signCSR(CSRView csrView) throws CAException {
if (csrView == null || Strings.isNullOrEmpty(csrView.getCsr())) {
throw new IllegalArgumentException("Empty CSR");
}
@@ -83,6 +86,7 @@ public Response signCSR(CSRView csrView) throws IOException, CAException {
@ApiOperation(value = "Revoke Project certificate")
@DELETE
@JWTRequired(acceptedTokens={Audience.SERVICES}, allowedUserRoles={"AGENT"})
+ @ApiKeyRequired(acceptedScopes = {ApiScope.AUTH}, allowedUserRoles = {"AGENT"})
public Response revokeCertificate(
@ApiParam(value = "Identifier of the Certificate to revoke", required = true) @QueryParam("certId") String certId)
throws IOException, CAException {
diff --git a/hopsworks-ca/src/main/java/io/hops/hopsworks/ca/api/filter/AuthFilter.java b/hopsworks-ca/src/main/java/io/hops/hopsworks/ca/api/filter/AuthFilter.java
index 6b68742857..2b2cbc5b80 100644
--- a/hopsworks-ca/src/main/java/io/hops/hopsworks/ca/api/filter/AuthFilter.java
+++ b/hopsworks-ca/src/main/java/io/hops/hopsworks/ca/api/filter/AuthFilter.java
@@ -18,6 +18,8 @@
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
+import io.hops.hopsworks.api.auth.key.ApiKeyFilter;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
import io.hops.hopsworks.ca.api.exception.mapper.CAJsonResponse;
import io.hops.hopsworks.ca.configuration.CAConf;
import io.hops.hopsworks.restutils.JsonResponse;
@@ -32,23 +34,29 @@
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
import javax.annotation.Priority;
import javax.ejb.EJB;
import javax.ws.rs.Priorities;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ResourceInfo;
import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.ext.Provider;
import static io.hops.hopsworks.ca.configuration.CAConf.CAConfKeys.JWT_ISSUER;
+import static io.hops.hopsworks.jwt.Constants.WWW_AUTHENTICATE_VALUE;
@Provider
@JWTRequired
@Priority(Priorities.AUTHENTICATION)
public class AuthFilter extends JWTFilter {
+ private final static Logger LOGGER = Logger.getLogger(AuthFilter.class.getName());
+
@EJB
private JWTController jwtController;
@EJB
@@ -73,6 +81,17 @@ public boolean isTokenValid(DecodedJWT jwt) {
@Override
public boolean preJWTFilter(ContainerRequestContext requestContext) throws IOException {
+ String authorizationHeader = requestContext.getHeaderString(HttpHeaders.AUTHORIZATION);
+ if (authorizationHeader != null && authorizationHeader.startsWith(ApiKeyFilter.API_KEY)) {
+ LOGGER.log(Level.FINEST, "{0} found, leaving JWT interceptor", ApiKeyFilter.API_KEY);
+ if (getApiKeyAnnotation() == null) {
+ requestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED).header(HttpHeaders.WWW_AUTHENTICATE,
+ WWW_AUTHENTICATE_VALUE).entity(responseEntity(Response.Status.UNAUTHORIZED,
+ "Authorization method not supported."))
+ .build());
+ }
+ return false;
+ }
return true;
}
@@ -132,4 +151,12 @@ public Object responseEntity(Response.Status status, String msg) {
jsonResponse.setErrorMsg(msg);
return jsonResponse;
}
+
+ private ApiKeyRequired getApiKeyAnnotation() {
+ Class> resourceClass = resourceInfo.getResourceClass();
+ Method method = resourceInfo.getResourceMethod();
+ ApiKeyRequired methodRolesAnnotation = method.getAnnotation(ApiKeyRequired.class);
+ ApiKeyRequired classRolesAnnotation = resourceClass.getAnnotation(ApiKeyRequired.class);
+ return methodRolesAnnotation != null ? methodRolesAnnotation : classRolesAnnotation;
+ }
}
diff --git a/hopsworks-ca/src/main/java/io/hops/hopsworks/ca/api/filter/CaApiKeyFilter.java b/hopsworks-ca/src/main/java/io/hops/hopsworks/ca/api/filter/CaApiKeyFilter.java
new file mode 100644
index 0000000000..7069bab72e
--- /dev/null
+++ b/hopsworks-ca/src/main/java/io/hops/hopsworks/ca/api/filter/CaApiKeyFilter.java
@@ -0,0 +1,84 @@
+/*
+ * This file is part of Hopsworks
+ * Copyright (C) 2023, Hopsworks AB. All rights reserved
+ *
+ * Hopsworks is free software: you can redistribute it and/or modify it under the terms of
+ * the GNU Affero General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * Hopsworks 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License along with this program.
+ * If not, see .
+ */
+
+package io.hops.hopsworks.ca.api.filter;
+
+import io.hops.hopsworks.api.auth.Configuration;
+import io.hops.hopsworks.api.auth.UserStatusValidator;
+import io.hops.hopsworks.api.auth.UserUtilities;
+import io.hops.hopsworks.api.auth.key.ApiKeyFilter;
+import io.hops.hopsworks.api.auth.key.ApiKeyRequired;
+import io.hops.hopsworks.api.auth.key.ApiKeyUtilities;
+import io.hops.hopsworks.exceptions.ApiKeyException;
+import io.hops.hopsworks.exceptions.UserException;
+import io.hops.hopsworks.persistence.entity.user.Users;
+import io.hops.hopsworks.persistence.entity.user.security.apiKey.ApiKey;
+import io.hops.hopsworks.persistence.entity.user.security.apiKey.ApiScope;
+import io.hops.hopsworks.restutils.RESTLogLevel;
+
+import javax.annotation.Priority;
+import javax.ejb.EJB;
+import javax.ws.rs.Priorities;
+import javax.ws.rs.container.ResourceInfo;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.ext.Provider;
+import java.lang.reflect.Method;
+import java.util.List;
+import java.util.Set;
+
+@Provider
+@ApiKeyRequired
+@Priority(Priorities.AUTHENTICATION - 1)
+public class CaApiKeyFilter extends ApiKeyFilter {
+ @EJB
+ private UserStatusValidator userStatusValidator;
+ @EJB
+ private ApiKeyUtilities apiKeyUtilities;
+ @EJB
+ private UserUtilities userUtilities;
+ @EJB
+ private Configuration conf;
+ @Context
+ private ResourceInfo resourceInfo;
+
+ protected void validateUserStatus(Users user) throws UserException {
+ userStatusValidator.checkStatus(user.getStatus());
+ }
+
+ protected ApiKey getApiKey(String key) throws ApiKeyException {
+ return apiKeyUtilities.getApiKey(key);
+ }
+
+ protected Set getApiScopes(ApiKey key) {
+ return apiKeyUtilities.getScopes(key);
+ }
+
+ protected List getUserRoles(Users user) {
+ return userUtilities.getUserRoles(user);
+ }
+
+ protected RESTLogLevel getRestLogLevel() {
+ return conf.getLogLevel(Configuration.AuthConfigurationKeys.HOPSWORKS_REST_LOG_LEVEL);
+ }
+
+ protected Class> getResourceClass() {
+ return resourceInfo.getResourceClass();
+ }
+
+ protected Method getResourceMethod() {
+ return resourceInfo.getResourceMethod();
+ }
+}
diff --git a/hopsworks-common/pom.xml b/hopsworks-common/pom.xml
index b26e5dd47f..285504f698 100644
--- a/hopsworks-common/pom.xml
+++ b/hopsworks-common/pom.xml
@@ -67,6 +67,12 @@
hopsworks-rest-utils
+
+ io.hops.hopsworks
+ hopsworks-api-auth
+ ejb
+
+
io.hops.hopsworks
hopsworks-jwt
diff --git a/hopsworks-common/src/main/java/io/hops/hopsworks/common/dao/AbstractFacade.java b/hopsworks-common/src/main/java/io/hops/hopsworks/common/dao/AbstractFacade.java
index 0ef24a9f6f..720c5022a9 100755
--- a/hopsworks-common/src/main/java/io/hops/hopsworks/common/dao/AbstractFacade.java
+++ b/hopsworks-common/src/main/java/io/hops/hopsworks/common/dao/AbstractFacade.java
@@ -39,287 +39,13 @@
package io.hops.hopsworks.common.dao;
-import io.hops.hopsworks.exceptions.InvalidQueryException;
-
-import javax.persistence.EntityManager;
-import javax.persistence.Query;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Set;
-
-public abstract class AbstractFacade {
-
- public static Integer BATCH_SIZE = 100;
-
- protected final Class entityClass;
+/*
+ * Only for compatibility purposes
+ */
+@Deprecated
+public abstract class AbstractFacade extends io.hops.hopsworks.persistence.entity.util.AbstractFacade {
public AbstractFacade(Class entityClass) {
- this.entityClass = entityClass;
- }
-
- protected abstract EntityManager getEntityManager();
-
- public void save(T entity) {
- getEntityManager().persist(entity);
- }
-
- public T update(T entity) {
- return getEntityManager().merge(entity);
- }
-
- public void remove(T entity) {
- if (entity == null) {
- return;
- }
- getEntityManager().remove(getEntityManager().merge(entity));
- getEntityManager().flush();
- }
-
- public void removeBatch(List entities) {
- for (T item : entities) {
- getEntityManager().remove(getEntityManager().merge(item));
- }
-
- getEntityManager().flush();
- }
-
- public T find(Object id) {
- return getEntityManager().find(entityClass, id);
- }
-
- public List findAll() {
- javax.persistence.criteria.CriteriaQuery cq = getEntityManager().
- getCriteriaBuilder().createQuery();
- cq.select(cq.from(entityClass));
- return getEntityManager().createQuery(cq).getResultList();
- }
-
- public List findRange(int[] range) {
- javax.persistence.criteria.CriteriaQuery cq = getEntityManager().
- getCriteriaBuilder().createQuery();
- cq.select(cq.from(entityClass));
- javax.persistence.Query q = getEntityManager().createQuery(cq);
- q.setMaxResults(range[1] - range[0]);
- q.setFirstResult(range[0]);
- return q.getResultList();
- }
-
- public long count() {
- javax.persistence.criteria.CriteriaQuery cq = getEntityManager().
- getCriteriaBuilder().createQuery();
- javax.persistence.criteria.Root rt = cq.from(entityClass);
- cq.select(getEntityManager().getCriteriaBuilder().count(rt)).where();
- javax.persistence.Query q = getEntityManager().createQuery(cq);
- return (Long) q.getSingleResult();
- }
-
- public void setOffsetAndLim(Integer offset, Integer limit, Query q) {
- if (offset != null && offset > 0) {
- q.setFirstResult(offset);
- }
- if (limit != null && limit > 0) {
- q.setMaxResults(limit);
- }
- }
-
- public String OrderBy(SortBy sortBy) {
- return sortBy.getSql() + " " + sortBy.getParam().getSql();
- }
-
- public String buildQuery(String query, Set extends AbstractFacade.FilterBy> filters,
- Set extends AbstractFacade.SortBy> sorts, String more) {
- return query + buildFilterString(filters, more) + buildSortString(sorts);
- }
-
- public String buildSortString(Set extends SortBy> sortBy) {
- if (sortBy == null || sortBy.isEmpty()) {
- return "";
- }
- sortBy.remove(null);
- Iterator extends SortBy> sort = sortBy.iterator();
- if (!sort.hasNext()) {
- return "";
- }
- StringBuilder c = new StringBuilder(" ORDER BY " + OrderBy(sort.next()));
- for (;sort.hasNext();) {
- c.append(", ").append(OrderBy(sort.next()));
- }
- return c.toString();
- }
-
- public String buildFilterString(Set extends FilterBy> filter, String more) {
- String s = more == null || more.isEmpty() ? "" : "WHERE " + more;
- if (filter == null || filter.isEmpty()) {
- return s;
- }
- filter.remove(null);
- Iterator extends FilterBy> filterBy = filter.iterator();
- if (!filterBy.hasNext()) {
- return s;
- }
- StringBuilder c = new StringBuilder(" WHERE " + filterBy.next().getSql());
- for (;filterBy.hasNext();) {
- c.append(" AND ").append(filterBy.next().getSql());
- }
- return c.append(more == null || more.isEmpty()? "": " AND " + more).toString();
- }
-
- public Date getDate(String field, String value) {
- String[] formats = {"yyyy-MM-dd'T'HH:mm:ss.SSSX", "yyyy-MM-dd'T'HH:mm:ss.SSSZ", "yyyy-MM-dd'T'HH:mm:ssX",
- "yyyy-MM-dd'T'HH:mm:ssZ", "yyyy-MM-dd'T'HH:mm:sss", "yyyy-MM-dd"};
- Date date = null;
- for (int i = 0; i < formats.length && date == null; i++ ) {
- date = getDateByFormat(value, formats[i]);
- }
- if (date == null) {
- throw new InvalidQueryException(
- "Filter value for " + field + " needs to set valid format. Expected:yyyy-MM-dd hh:mm:ss.SSSX but found: " +
- value);
- }
- return date;
- }
-
- private Date getDateByFormat(String value, String format) {
- SimpleDateFormat sdf = new SimpleDateFormat(format);
- try {
- return sdf.parse(value);
- } catch (ParseException e) {
- return null;
- }
- }
-
- public Integer getIntValue(FilterBy filterBy) {
- return getIntValue(filterBy.getField(), filterBy.getParam());
- }
-
- public Integer getIntValue(String field, String value) {
- Integer val;
- try {
- val = Integer.parseInt(value);
- } catch (NumberFormatException e) {
- throw new InvalidQueryException("Filter value for " + field + " needs to set an Integer, but found: " + value);
- }
- return val;
- }
-
- public Long getLongValue(String field, String value) {
- try {
- return Long.parseLong(value);
- } catch (NumberFormatException e) {
- throw new InvalidQueryException("Filter value for " + field + " needs to set a Long, but found: " + value);
- }
- }
-
- public List getIntValues(FilterBy filterBy) {
- String[] filterStrs = splitFilterParams(filterBy);
- List values = new ArrayList<>();
- String field = filterBy.getField();
- Integer val;
- for (String filterStr : filterStrs) {
- val = getIntValue(field, filterStr);
- values.add(val);
- }
- return values;
- }
-
- public boolean getBooleanValue(String value) {
- return "1".equals(value) || "true".equalsIgnoreCase(value);
- }
-
- public > List getEnumValues(FilterBy filterBy, final Class enumType) {
- String[] filterStrs = splitFilterParams(filterBy);
- List enumObjects = new ArrayList<>();
- String field = filterBy.getField();
- E enumObject;
- for (String filterStr : filterStrs) {
- enumObject = getEnumValue(field, filterStr, enumType);
- enumObjects.add(enumObject);
- }
- return enumObjects;
- }
-
- public > E getEnumValue(String field, String filterStr, final Class enumType) {
- E enumObject;
- try {
- enumObject = E.valueOf(enumType, filterStr);
- } catch (IllegalArgumentException iae) {
- throw new InvalidQueryException("Filter value for " + field + " needs to set valid " + field + ", but found: "
- + filterStr, iae);
- }
- return enumObject;
- }
-
- public String[] splitFilterParams(FilterBy filterBy) {
- return filterBy.getParam().split(",");
- }
-
- public interface SortBy {
- String getValue();
- OrderBy getParam();
- String getSql();
- }
-
- public interface FilterBy {
- String getValue();
- String getParam();
- String getSql();
- String getField();
- }
-
- public enum OrderBy {
- ASC ("ASC", "ASC"),
- DESC ("DESC", "DESC");
-
- private final String value;
- private final String sql;
-
- private OrderBy(String value, String sql) {
- this.value = value;
- this.sql = sql;
- }
-
- public String getValue() {
- return value;
- }
-
- public String getSql() {
- return sql;
- }
-
- @Override
- public String toString() {
- return value;
- }
-
- }
-
- public static class CollectionInfo {
- private Long count;
- private List items;
-
- public CollectionInfo(Long count, List items) {
- this.count = count;
- this.items = items;
- }
-
- public Long getCount() {
- return count;
- }
-
- public List getItems() {
- return items;
- }
-
- public void setItems(List items) {
- this.items = items;
- }
-
- public void setCount(Long count) {
- this.count = count;
- }
+ super(entityClass);
}
}
diff --git a/hopsworks-common/src/main/java/io/hops/hopsworks/common/dao/hdfs/inode/InodeFacade.java b/hopsworks-common/src/main/java/io/hops/hopsworks/common/dao/hdfs/inode/InodeFacade.java
index 945ecd5c3d..82624e231b 100644
--- a/hopsworks-common/src/main/java/io/hops/hopsworks/common/dao/hdfs/inode/InodeFacade.java
+++ b/hopsworks-common/src/main/java/io/hops/hopsworks/common/dao/hdfs/inode/InodeFacade.java
@@ -40,6 +40,7 @@
package io.hops.hopsworks.common.dao.hdfs.inode;
import io.hops.hopsworks.common.dao.AbstractFacade;
+import io.hops.hopsworks.persistence.InvalidQueryException;
import io.hops.hopsworks.persistence.entity.hdfs.user.HdfsUsers;
import io.hops.hopsworks.common.dao.hdfsUser.HdfsUsersFacade;
import io.hops.hopsworks.persistence.entity.project.Project;
@@ -47,7 +48,6 @@
import io.hops.hopsworks.persistence.entity.user.Users;
import io.hops.hopsworks.common.hdfs.HdfsUsersController;
import io.hops.hopsworks.common.util.HopsUtils;
-import io.hops.hopsworks.exceptions.InvalidQueryException;
import io.hops.hopsworks.persistence.entity.hdfs.inode.Inode;
import io.hops.hopsworks.persistence.entity.hdfs.inode.InodePK;
diff --git a/hopsworks-common/src/main/java/io/hops/hopsworks/common/dao/jobhistory/ExecutionFacade.java b/hopsworks-common/src/main/java/io/hops/hopsworks/common/dao/jobhistory/ExecutionFacade.java
index 2897180851..fef3db75db 100644
--- a/hopsworks-common/src/main/java/io/hops/hopsworks/common/dao/jobhistory/ExecutionFacade.java
+++ b/hopsworks-common/src/main/java/io/hops/hopsworks/common/dao/jobhistory/ExecutionFacade.java
@@ -40,13 +40,13 @@
package io.hops.hopsworks.common.dao.jobhistory;
import io.hops.hopsworks.common.dao.AbstractFacade;
+import io.hops.hopsworks.persistence.InvalidQueryException;
import io.hops.hopsworks.persistence.entity.jobs.description.Jobs;
import io.hops.hopsworks.persistence.entity.project.Project;
import io.hops.hopsworks.persistence.entity.user.Users;
import io.hops.hopsworks.persistence.entity.jobs.configuration.JobType;
import io.hops.hopsworks.persistence.entity.jobs.configuration.history.JobFinalStatus;
import io.hops.hopsworks.persistence.entity.jobs.configuration.history.JobState;
-import io.hops.hopsworks.exceptions.InvalidQueryException;
import io.hops.hopsworks.persistence.entity.jobs.history.Execution;
import org.javatuples.Pair;
diff --git a/hopsworks-common/src/main/java/io/hops/hopsworks/common/dao/jobs/description/JobFacade.java b/hopsworks-common/src/main/java/io/hops/hopsworks/common/dao/jobs/description/JobFacade.java
index 7de819c476..db96991566 100644
--- a/hopsworks-common/src/main/java/io/hops/hopsworks/common/dao/jobs/description/JobFacade.java
+++ b/hopsworks-common/src/main/java/io/hops/hopsworks/common/dao/jobs/description/JobFacade.java
@@ -40,9 +40,9 @@
package io.hops.hopsworks.common.dao.jobs.description;
import io.hops.hopsworks.common.dao.AbstractFacade;
+import io.hops.hopsworks.persistence.InvalidQueryException;
import io.hops.hopsworks.persistence.entity.project.Project;
import io.hops.hopsworks.persistence.entity.user.Users;
-import io.hops.hopsworks.exceptions.InvalidQueryException;
import io.hops.hopsworks.persistence.entity.jobs.configuration.JobConfiguration;
import io.hops.hopsworks.persistence.entity.jobs.configuration.ScheduleDTO;
import io.hops.hopsworks.persistence.entity.jobs.configuration.history.JobState;
diff --git a/hopsworks-common/src/main/java/io/hops/hopsworks/common/dao/kagent/HostServicesFacade.java b/hopsworks-common/src/main/java/io/hops/hopsworks/common/dao/kagent/HostServicesFacade.java
index 3e9f5a7685..2cd74f0f47 100644
--- a/hopsworks-common/src/main/java/io/hops/hopsworks/common/dao/kagent/HostServicesFacade.java
+++ b/hopsworks-common/src/main/java/io/hops/hopsworks/common/dao/kagent/HostServicesFacade.java
@@ -39,10 +39,10 @@
package io.hops.hopsworks.common.dao.kagent;
+import io.hops.hopsworks.persistence.InvalidQueryException;
import io.hops.hopsworks.persistence.entity.host.ServiceStatus;
import io.hops.hopsworks.persistence.entity.kagent.HostServices;
import io.hops.hopsworks.common.dao.AbstractFacade;
-import io.hops.hopsworks.exceptions.InvalidQueryException;
import java.util.List;
import javax.ejb.Stateless;
diff --git a/hopsworks-common/src/main/java/io/hops/hopsworks/common/dao/user/UserFacade.java b/hopsworks-common/src/main/java/io/hops/hopsworks/common/dao/user/UserFacade.java
index a474edab10..e30de0a04a 100644
--- a/hopsworks-common/src/main/java/io/hops/hopsworks/common/dao/user/UserFacade.java
+++ b/hopsworks-common/src/main/java/io/hops/hopsworks/common/dao/user/UserFacade.java
@@ -39,7 +39,7 @@
package io.hops.hopsworks.common.dao.user;
import io.hops.hopsworks.common.dao.AbstractFacade;
-import io.hops.hopsworks.exceptions.InvalidQueryException;
+import io.hops.hopsworks.persistence.InvalidQueryException;
import io.hops.hopsworks.persistence.entity.user.BbcGroup;
import io.hops.hopsworks.persistence.entity.user.Users;
import io.hops.hopsworks.persistence.entity.user.security.UserGroup;
diff --git a/hopsworks-common/src/main/java/io/hops/hopsworks/common/featurestore/datavalidationv2/expectations/ExpectationController.java b/hopsworks-common/src/main/java/io/hops/hopsworks/common/featurestore/datavalidationv2/expectations/ExpectationController.java
index ee065fc4d4..78b8609628 100644
--- a/hopsworks-common/src/main/java/io/hops/hopsworks/common/featurestore/datavalidationv2/expectations/ExpectationController.java
+++ b/hopsworks-common/src/main/java/io/hops/hopsworks/common/featurestore/datavalidationv2/expectations/ExpectationController.java
@@ -17,7 +17,6 @@
package io.hops.hopsworks.common.featurestore.datavalidationv2.expectations;
-import io.hops.hopsworks.common.dao.AbstractFacade.CollectionInfo;
import io.hops.hopsworks.common.featurestore.activity.FeaturestoreActivityFacade;
import io.hops.hopsworks.common.featurestore.featuregroup.FeaturegroupController;
import io.hops.hopsworks.exceptions.FeaturestoreException;
@@ -25,6 +24,7 @@
import io.hops.hopsworks.persistence.entity.featurestore.featuregroup.datavalidationv2.Expectation;
import io.hops.hopsworks.persistence.entity.featurestore.featuregroup.datavalidationv2.ExpectationSuite;
import io.hops.hopsworks.persistence.entity.user.Users;
+import io.hops.hopsworks.persistence.entity.util.AbstractFacade;
import io.hops.hopsworks.restutils.RESTCodes;
import org.json.JSONArray;
@@ -141,7 +141,8 @@ public void deleteExpectation(Users user, Integer expectationId, boolean logActi
}
}
- public CollectionInfo getExpectationsByExpectationSuite(ExpectationSuite expectationSuite) {
+ public AbstractFacade.CollectionInfo getExpectationsByExpectationSuite(
+ ExpectationSuite expectationSuite) {
return expectationFacade.findByExpectationSuite(expectationSuite);
}
diff --git a/hopsworks-common/src/main/java/io/hops/hopsworks/common/featurestore/datavalidationv2/reports/ValidationReportController.java b/hopsworks-common/src/main/java/io/hops/hopsworks/common/featurestore/datavalidationv2/reports/ValidationReportController.java
index b7e05ac643..fc7b95b093 100644
--- a/hopsworks-common/src/main/java/io/hops/hopsworks/common/featurestore/datavalidationv2/reports/ValidationReportController.java
+++ b/hopsworks-common/src/main/java/io/hops/hopsworks/common/featurestore/datavalidationv2/reports/ValidationReportController.java
@@ -18,9 +18,6 @@
import io.hops.hopsworks.alerting.api.alert.dto.PostableAlert;
import io.hops.hopsworks.common.alert.AlertController;
-import io.hops.hopsworks.common.dao.AbstractFacade.CollectionInfo;
-import io.hops.hopsworks.common.dao.AbstractFacade.FilterBy;
-import io.hops.hopsworks.common.dao.AbstractFacade.SortBy;
import io.hops.hopsworks.common.dataset.DatasetController;
import io.hops.hopsworks.common.featurestore.FeaturestoreController;
import io.hops.hopsworks.common.featurestore.activity.FeaturestoreActivityFacade;
@@ -48,6 +45,7 @@
import io.hops.hopsworks.persistence.entity.project.alert.ProjectServiceAlertStatus;
import io.hops.hopsworks.persistence.entity.project.service.ProjectServiceEnum;
import io.hops.hopsworks.persistence.entity.user.Users;
+import io.hops.hopsworks.persistence.entity.util.AbstractFacade;
import io.hops.hopsworks.restutils.RESTCodes;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.fs.permission.FsPermission;
@@ -116,8 +114,9 @@ public ValidationReport getValidationReportById(Integer validationReportId)
return validationReport.get();
}
- public CollectionInfo getAllValidationReportByFeatureGroup(Integer offset, Integer limit,
- Set extends SortBy> sorts, Set extends FilterBy> filters, Featuregroup featuregroup) {
+ public AbstractFacade.CollectionInfo getAllValidationReportByFeatureGroup(
+ Integer offset, Integer limit, Set extends AbstractFacade.SortBy> sorts,
+ Set extends AbstractFacade.FilterBy> filters, Featuregroup featuregroup) {
return validationReportFacade.findByFeaturegroup(offset, limit, sorts, filters, featuregroup);
}
diff --git a/hopsworks-common/src/main/java/io/hops/hopsworks/common/featurestore/datavalidationv2/results/ValidationResultController.java b/hopsworks-common/src/main/java/io/hops/hopsworks/common/featurestore/datavalidationv2/results/ValidationResultController.java
index d6fa881300..d946a34718 100644
--- a/hopsworks-common/src/main/java/io/hops/hopsworks/common/featurestore/datavalidationv2/results/ValidationResultController.java
+++ b/hopsworks-common/src/main/java/io/hops/hopsworks/common/featurestore/datavalidationv2/results/ValidationResultController.java
@@ -16,15 +16,13 @@
package io.hops.hopsworks.common.featurestore.datavalidationv2.results;
-import io.hops.hopsworks.common.dao.AbstractFacade.CollectionInfo;
-import io.hops.hopsworks.common.dao.AbstractFacade.FilterBy;
-import io.hops.hopsworks.common.dao.AbstractFacade.SortBy;
import io.hops.hopsworks.common.featurestore.FeaturestoreFacade;
import io.hops.hopsworks.common.featurestore.datavalidationv2.expectations.ExpectationFacade;
import io.hops.hopsworks.exceptions.FeaturestoreException;
import io.hops.hopsworks.persistence.entity.featurestore.featuregroup.datavalidationv2.Expectation;
import io.hops.hopsworks.persistence.entity.featurestore.featuregroup.datavalidationv2.ValidationReport;
import io.hops.hopsworks.persistence.entity.featurestore.featuregroup.datavalidationv2.ValidationResult;
+import io.hops.hopsworks.persistence.entity.util.AbstractFacade;
import io.hops.hopsworks.restutils.RESTCodes;
import org.json.JSONException;
import org.json.JSONObject;
@@ -63,8 +61,9 @@ public class ValidationResultController {
@EJB
private FeaturestoreFacade featurestoreFacade;
- public CollectionInfo getAllValidationResultByExpectationId (Integer offset, Integer limit,
- Set extends SortBy> sorts, Set extends FilterBy> filters, Integer expectationId) {
+ public AbstractFacade.CollectionInfo getAllValidationResultByExpectationId (
+ Integer offset, Integer limit, Set extends AbstractFacade.SortBy> sorts,
+ Set extends AbstractFacade.FilterBy> filters, Integer expectationId) {
Optional optExpectation = expectationFacade.findById(expectationId);
Expectation expectation;
diff --git a/hopsworks-common/src/main/java/io/hops/hopsworks/common/featurestore/datavalidationv2/suites/ExpectationSuiteController.java b/hopsworks-common/src/main/java/io/hops/hopsworks/common/featurestore/datavalidationv2/suites/ExpectationSuiteController.java
index c106ed9f33..49e432f4e1 100644
--- a/hopsworks-common/src/main/java/io/hops/hopsworks/common/featurestore/datavalidationv2/suites/ExpectationSuiteController.java
+++ b/hopsworks-common/src/main/java/io/hops/hopsworks/common/featurestore/datavalidationv2/suites/ExpectationSuiteController.java
@@ -17,7 +17,6 @@
package io.hops.hopsworks.common.featurestore.datavalidationv2.suites;
import com.google.common.base.Strings;
-import io.hops.hopsworks.common.dao.AbstractFacade.CollectionInfo;
import io.hops.hopsworks.common.featurestore.activity.FeaturestoreActivityFacade;
import io.hops.hopsworks.common.featurestore.datavalidationv2.expectations.ExpectationController;
import io.hops.hopsworks.common.featurestore.datavalidationv2.expectations.ExpectationDTO;
@@ -30,6 +29,7 @@
import io.hops.hopsworks.persistence.entity.featurestore.featuregroup.datavalidationv2.ExpectationSuite;
import io.hops.hopsworks.persistence.entity.featurestore.featuregroup.datavalidationv2.GreatExpectation;
import io.hops.hopsworks.persistence.entity.user.Users;
+import io.hops.hopsworks.persistence.entity.util.AbstractFacade;
import io.hops.hopsworks.restutils.RESTCodes;
import org.json.JSONException;
import org.json.JSONObject;
@@ -71,7 +71,7 @@ public class ExpectationSuiteController {
////// Great Expectations
//////////////////////////////////////////////////
- public CollectionInfo getAllGreatExpectations() {
+ public AbstractFacade.CollectionInfo getAllGreatExpectations() {
return greatExpectationFacade.findAllGreatExpectation();
}
diff --git a/hopsworks-common/src/main/java/io/hops/hopsworks/common/featurestore/featuregroup/cached/FeatureGroupCommitController.java b/hopsworks-common/src/main/java/io/hops/hopsworks/common/featurestore/featuregroup/cached/FeatureGroupCommitController.java
index 0598ee4e78..3e1142ddd4 100644
--- a/hopsworks-common/src/main/java/io/hops/hopsworks/common/featurestore/featuregroup/cached/FeatureGroupCommitController.java
+++ b/hopsworks-common/src/main/java/io/hops/hopsworks/common/featurestore/featuregroup/cached/FeatureGroupCommitController.java
@@ -16,12 +16,11 @@
package io.hops.hopsworks.common.featurestore.featuregroup.cached;
-import io.hops.hopsworks.common.dao.AbstractFacade;
import io.hops.hopsworks.common.featurestore.activity.FeaturestoreActivityFacade;
-import io.hops.hopsworks.common.dao.AbstractFacade.CollectionInfo;
import io.hops.hopsworks.persistence.entity.featurestore.featuregroup.Featuregroup;
import io.hops.hopsworks.persistence.entity.featurestore.featuregroup.cached.FeatureGroupCommit;
import io.hops.hopsworks.persistence.entity.user.Users;
+import io.hops.hopsworks.persistence.entity.util.AbstractFacade;
import javax.ejb.EJB;
import javax.ejb.Stateless;
@@ -88,7 +87,8 @@ public Integer countCommitsInRange(Featuregroup featuregroup, Long startTimestam
return featureGroupCommitFacade.countCommitsInRange(featuregroup.getId(), startTimestamp, endTimestamp);
}
- public CollectionInfo getCommitDetails(Integer featureGroupId, Integer limit, Integer offset,
+ public AbstractFacade.CollectionInfo getCommitDetails(
+ Integer featureGroupId, Integer limit, Integer offset,
Set extends AbstractFacade.SortBy> sort,
Set extends AbstractFacade.FilterBy> filters) {
diff --git a/hopsworks-common/src/main/java/io/hops/hopsworks/common/git/GitJWTManager.java b/hopsworks-common/src/main/java/io/hops/hopsworks/common/git/GitJWTManager.java
index 892912481b..5653436ecb 100644
--- a/hopsworks-common/src/main/java/io/hops/hopsworks/common/git/GitJWTManager.java
+++ b/hopsworks-common/src/main/java/io/hops/hopsworks/common/git/GitJWTManager.java
@@ -15,7 +15,7 @@
*/
package io.hops.hopsworks.common.git;
-import io.hops.hopsworks.common.user.UsersController;
+import io.hops.hopsworks.api.auth.UserUtilities;
import io.hops.hopsworks.common.util.DateUtils;
import io.hops.hopsworks.common.util.Settings;
import io.hops.hopsworks.exceptions.GitOpException;
@@ -51,7 +51,7 @@ public class GitJWTManager {
@EJB
private Settings settings;
@EJB
- private UsersController usersController;
+ private UserUtilities userUtilities;
private final String TOKEN_FILE_NAME = "token.jwt";
@@ -69,7 +69,7 @@ public void materializeJWT(Users user, String tokenPath) throws GitOpException {
private String createTokenForGitContainer(Users user, LocalDateTime expirationDate)
throws GitOpException {
- String[] userRoles = usersController.getUserRoles(user).toArray(new String[1]);
+ String[] userRoles = userUtilities.getUserRoles(user).toArray(new String[1]);
return createTokenForGitContainer(user.getUsername(), userRoles, expirationDate);
}
diff --git a/hopsworks-common/src/main/java/io/hops/hopsworks/common/jupyter/JupyterJWTManager.java b/hopsworks-common/src/main/java/io/hops/hopsworks/common/jupyter/JupyterJWTManager.java
index b4c99039a8..93cb8b3fee 100644
--- a/hopsworks-common/src/main/java/io/hops/hopsworks/common/jupyter/JupyterJWTManager.java
+++ b/hopsworks-common/src/main/java/io/hops/hopsworks/common/jupyter/JupyterJWTManager.java
@@ -18,12 +18,12 @@
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.interfaces.DecodedJWT;
+import io.hops.hopsworks.api.auth.UserUtilities;
import io.hops.hopsworks.common.dao.jupyter.MaterializedJWTFacade;
import io.hops.hopsworks.common.dao.jupyter.JupyterSettingsFacade;
import io.hops.hopsworks.common.dao.jupyter.config.JupyterFacade;
import io.hops.hopsworks.common.dao.project.ProjectFacade;
import io.hops.hopsworks.common.dao.user.UserFacade;
-import io.hops.hopsworks.common.user.UsersController;
import io.hops.hopsworks.common.util.DateUtils;
import io.hops.hopsworks.common.util.PayaraClusterManager;
import io.hops.hopsworks.common.util.Settings;
@@ -91,7 +91,7 @@ public class JupyterJWTManager {
@EJB
private JWTController jwtController;
@EJB
- private UsersController usersController;
+ private UserUtilities userUtilities;
@EJB
private JupyterFacade jupyterFacade;
@Inject
@@ -190,7 +190,7 @@ protected void recover() {
// We should create a new one
String[] audience = new String[]{"api"};
LocalDateTime expirationDate = LocalDateTime.now().plus(settings.getJWTLifetimeMs(), ChronoUnit.MILLIS);
- String[] userRoles = usersController.getUserRoles(user).toArray(new String[1]);
+ String[] userRoles = userUtilities.getUserRoles(user).toArray(new String[1]);
try {
Map claims = new HashMap<>(3);
claims.put(Constants.RENEWABLE, false);
@@ -249,7 +249,7 @@ public void materializeJWT(Users user, Project project, JupyterSettings jupyterS
LocalDateTime expirationDate = LocalDateTime.now().plus(settings.getJWTLifetimeMs(), ChronoUnit.MILLIS);
JupyterJWT jupyterJWT = new JupyterJWT(project, user, expirationDate, new CidAndPort(cid, port));
try {
- String[] roles = usersController.getUserRoles(user).toArray(new String[1]);
+ String[] roles = userUtilities.getUserRoles(user).toArray(new String[1]);
MaterializedJWT materializedJWT = new MaterializedJWT(materialID);
materializedJWTFacade.persist(materializedJWT);
diff --git a/hopsworks-common/src/main/java/io/hops/hopsworks/common/proxies/client/HttpClient.java b/hopsworks-common/src/main/java/io/hops/hopsworks/common/proxies/client/HttpClient.java
index 544beab0c3..9ec0b774a9 100644
--- a/hopsworks-common/src/main/java/io/hops/hopsworks/common/proxies/client/HttpClient.java
+++ b/hopsworks-common/src/main/java/io/hops/hopsworks/common/proxies/client/HttpClient.java
@@ -53,8 +53,7 @@
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
@ConcurrencyManagement(ConcurrencyManagementType.BEAN)
public class HttpClient {
-
- private static final String AUTH_HEADER_CONTENT = "Bearer %s";
+ private static final String API_KEY_AUTH_HEADER = "ApiKey %s";
@EJB
private Settings settings;
@@ -178,7 +177,7 @@ private PoolingHttpClientConnectionManager createConnectionManager(
public void setAuthorizationHeader(HttpRequest httpRequest) {
httpRequest.setHeader(HttpHeaders.AUTHORIZATION,
- String.format(AUTH_HEADER_CONTENT, settings.getServiceMasterJWT()));
+ String.format(API_KEY_AUTH_HEADER, settings.getServiceApiKey()));
}
public ObjectMapper getObjectMapper() {
diff --git a/hopsworks-common/src/main/java/io/hops/hopsworks/common/security/ServiceJWTKeepAlive.java b/hopsworks-common/src/main/java/io/hops/hopsworks/common/security/ServiceJWTKeepAlive.java
deleted file mode 100644
index 3516fbd6d9..0000000000
--- a/hopsworks-common/src/main/java/io/hops/hopsworks/common/security/ServiceJWTKeepAlive.java
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- * This file is part of Hopsworks
- * Copyright (C) 2018, Logical Clocks AB. All rights reserved
- *
- * Hopsworks is free software: you can redistribute it and/or modify it under the terms of
- * the GNU Affero General Public License as published by the Free Software Foundation,
- * either version 3 of the License, or (at your option) any later version.
- *
- * Hopsworks 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 Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License along with this program.
- * If not, see .
- */
-
-package io.hops.hopsworks.common.security;
-
-import com.auth0.jwt.interfaces.DecodedJWT;
-import com.google.common.base.Strings;
-import io.hops.hopsworks.common.util.DateUtils;
-import io.hops.hopsworks.common.util.PayaraClusterManager;
-import io.hops.hopsworks.common.util.Settings;
-import io.hops.hopsworks.jwt.JWTController;
-import io.hops.hopsworks.jwt.exception.JWTException;
-import org.apache.commons.lang3.tuple.Pair;
-import org.apache.hadoop.util.BackOff;
-import org.apache.hadoop.util.ExponentialBackOff;
-
-import javax.annotation.PostConstruct;
-import javax.annotation.PreDestroy;
-import javax.annotation.Resource;
-import javax.ejb.DependsOn;
-import javax.ejb.EJB;
-import javax.ejb.Lock;
-import javax.ejb.LockType;
-import javax.ejb.Singleton;
-import javax.ejb.Startup;
-import javax.ejb.Timeout;
-import javax.ejb.Timer;
-import javax.ejb.TimerConfig;
-import javax.ejb.TimerService;
-import javax.ejb.TransactionAttribute;
-import javax.ejb.TransactionAttributeType;
-import java.security.NoSuchAlgorithmException;
-import java.time.LocalDateTime;
-import java.time.temporal.ChronoUnit;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Date;
-import java.util.List;
-import java.util.concurrent.TimeUnit;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-@Singleton
-@Startup
-@DependsOn("Settings")
-@TransactionAttribute(TransactionAttributeType.NEVER)
-public class ServiceJWTKeepAlive {
-
- private final static Logger LOGGER = Logger.getLogger(ServiceJWTKeepAlive.class.getName());
- private final static List SERVICE_RENEW_JWT_AUDIENCE = new ArrayList<>(1);
- static {
- SERVICE_RENEW_JWT_AUDIENCE.add("services");
- }
-
- @EJB
- private Settings settings;
- @EJB
- private JWTController jwtController;
- @EJB
- private PayaraClusterManager payaraClusterManager;
- @Resource
- private TimerService timerService;
- private Timer timer;
-
- private String hostname;
- private BackOff backOff;
-
- @PostConstruct
- @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
- public void init() {
- hostname = "hopsworks";
- backOff = new ExponentialBackOff.Builder()
- .setInitialIntervalMillis(100L)
- .setMaximumIntervalMillis(2000L)
- .setMultiplier(2)
- // We issue five renewal tokens so we have four retries
- .setMaximumRetries(4)
- .build();
-
- // Do not whirl like a sufi
- long interval = Math.min(10000,
- Math.max(500L, settings.getServiceJWTLifetimeMS() / 2));
- timer = timerService.createIntervalTimer(5000L, interval, new TimerConfig("Service JWT renewer", false));
- }
-
- @PreDestroy
- private void destroyTimer() {
- if (timer != null) {
- timer.cancel();
- }
- }
-
- @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
- @Timeout
- @Lock(LockType.WRITE)
- public void renewServiceToken() {
- if (!payaraClusterManager.amIThePrimary()) {
- return;
- }
- try {
- doRenew(false);
- } catch (Exception ex) {
- LOGGER.log(Level.SEVERE, "Error renewing service JWT", ex);
- }
- }
-
- @Lock(LockType.WRITE)
- public void forceRenewServiceToken() throws JWTException {
- try {
- doRenew(true);
- } catch (InterruptedException ex) {
- LOGGER.log(Level.SEVERE, "Could not renew service JWT", ex);
- throw new JWTException(ex.getMessage(), ex);
- }
- }
-
- private void doRenew(boolean force)
- throws JWTException, InterruptedException {
- String masterToken = settings.getServiceMasterJWT();
- if (Strings.isNullOrEmpty(masterToken)) {
- throw new JWTException("Master token is empty!");
- }
- LocalDateTime now = DateUtils.getNow();
- DecodedJWT masterJWT = jwtController.decodeToken(masterToken);
- if (force || maybeRenewMasterToken(masterJWT, now)) {
- String[] renewalTokens = settings.getServiceRenewJWTs();
- List masterJWTRoles = getJWTRoles(masterJWT);
- String user = masterJWT.getSubject();
-
- backOff.reset();
- int renewIdx = 0;
- while (renewIdx < renewalTokens.length) {
- String oneTimeToken = renewalTokens[renewIdx];
- Date notBefore = DateUtils.localDateTime2Date(now);
- LocalDateTime expiresAt = now.plus(settings.getServiceJWTLifetimeMS(), ChronoUnit.MILLIS);
- try {
- Pair renewedTokens = jwtController.renewServiceToken(oneTimeToken, masterToken,
- DateUtils.localDateTime2Date(expiresAt), notBefore, settings.getServiceJWTLifetimeMS(), user,
- masterJWTRoles, SERVICE_RENEW_JWT_AUDIENCE, hostname, settings.getJWTIssuer(),
- settings.getJWTSigningKeyName(), force);
- LOGGER.log(Level.FINEST, "New master JWT: " + renewedTokens.getLeft());
- updateTokens(renewedTokens);
- LOGGER.log(Level.FINEST, "Invalidating JWT: " + masterToken);
- jwtController.invalidateServiceToken(masterToken, settings.getJWTSigningKeyName());
- break;
- } catch (JWTException | NoSuchAlgorithmException ex) {
- renewIdx++;
- Long backoffTimeout = backOff.getBackOffInMillis();
- if (backoffTimeout != -1) {
- LOGGER.log(Level.WARNING, "Failed to renew service JWT, retrying in {0} ms. {1}",
- new Object[]{backoffTimeout, ex.getMessage()});
- TimeUnit.MILLISECONDS.sleep(backoffTimeout);
- } else {
- backOff.reset();
- throw new JWTException("Cannot renew service JWT", ex);
- }
- }
- }
- LOGGER.log(Level.FINE, "Successfully renewed service JWT");
- }
- }
-
- private void updateTokens(Pair tokens) {
- settings.setServiceMasterJWT(tokens.getLeft());
- settings.setServiceRenewJWTs(tokens.getRight());
- }
-
- private List getJWTRoles(DecodedJWT jwt) {
- String[] rolesArray = jwtController.getRolesClaim(jwt);
- return Arrays.asList(rolesArray);
- }
-
- private boolean maybeRenewMasterToken(DecodedJWT jwt, LocalDateTime now) {
- LocalDateTime expiresAt = DateUtils.date2LocalDateTime(jwt.getExpiresAt());
- return expiresAt.isBefore(now) || expiresAt.isEqual(now);
- }
-}
diff --git a/hopsworks-common/src/main/java/io/hops/hopsworks/common/security/utils/Secret.java b/hopsworks-common/src/main/java/io/hops/hopsworks/common/security/utils/Secret.java
index b40203ab9c..e279ad055a 100644
--- a/hopsworks-common/src/main/java/io/hops/hopsworks/common/security/utils/Secret.java
+++ b/hopsworks-common/src/main/java/io/hops/hopsworks/common/security/utils/Secret.java
@@ -15,128 +15,22 @@
*/
package io.hops.hopsworks.common.security.utils;
-import org.apache.commons.codec.digest.DigestUtils;
+public final class Secret extends io.hops.hopsworks.api.auth.Secret {
-public final class Secret {
-
- private static final String KEY_ID_SEPARATOR = ".";
- public static final String KEY_ID_SEPARATOR_REGEX = "\\.";
- private final String prefix;
- private final String secret;
- private final String salt;
- private final int prefixMinLength;
- private final int secretMinLength;
- private final int saltMinLength;
- private final boolean prefixed;
-
public Secret(String prefix, String secret, String salt, int prefixMinLength, int secretMinLength,
- int saltMinLength) {
- this.prefix = prefix;
- this.secret = secret;
- this.salt = salt;
- this.prefixMinLength = prefixMinLength;
- this.secretMinLength = secretMinLength;
- this.saltMinLength = saltMinLength;
- this.prefixed = true;
+ int saltMinLength) {
+ super(prefix, secret, salt, prefixMinLength, secretMinLength, saltMinLength);
}
-
+
public Secret(String prefix, String secret, String salt) {
- this.prefix = prefix;
- this.secret = secret;
- this.salt = salt;
- this.prefixMinLength = 0;
- this.secretMinLength = 0;
- this.saltMinLength = 0;
- this.prefixed = true;
+ super(prefix, secret, salt);
}
-
+
public Secret(String secret, String salt, int secretMinLength, int saltMinLength) {
- this.prefix = "";
- this.secret = secret;
- this.salt = salt;
- this.prefixMinLength = 0;
- this.secretMinLength = secretMinLength;
- this.saltMinLength = saltMinLength;
- this.prefixed = false;
+ super(secret, salt, secretMinLength, saltMinLength);
}
-
+
public Secret(String secret, String salt) {
- this.prefix = "";
- this.secret = secret;
- this.salt = salt;
- this.prefixMinLength = 0;
- this.secretMinLength = 0;
- this.saltMinLength = 0;
- this.prefixed = false;
- }
-
- public String getPrefix() {
- return prefix;
- }
-
- public String getSecret() {
- return secret;
- }
-
- public String getSalt() {
- return salt;
- }
-
- public String getSecretPlusSalt() {
- return this.secret + this.salt;
- }
-
- public int getPrefixMinLength() {
- return prefixMinLength;
- }
-
- public int getSecretMinLength() {
- return secretMinLength;
- }
-
- public int getSaltMinLength() {
- return saltMinLength;
- }
-
- public boolean isPrefixed() {
- return prefixed;
- }
-
- public String getPrefixPlusSecret() {
- return this.prefix + KEY_ID_SEPARATOR + this.secret;
- }
-
- public String getSha256HexDigest() {
- String secPlusSalt = getSecretPlusSalt();
- return DigestUtils.sha256Hex(secPlusSalt);
- }
-
- public String getSha512HexDigest() {
- String secPlusSalt = getSecretPlusSalt();
- return DigestUtils.sha512Hex(secPlusSalt);
- }
-
- public String getSha1HexDigest() {
- String secPlusSalt = getSecretPlusSalt();
- return DigestUtils.sha1Hex(secPlusSalt);
- }
-
- public boolean validateNotNullOrEmpty() {
- if (this.prefixed) {
- return this.prefix != null && !this.prefix.isEmpty() && this.secret != null && !this.secret.isEmpty() &&
- this.salt != null && !this.salt.isEmpty();
- } else {
- return this.secret != null && !this.secret.isEmpty() && this.salt != null && !this.salt.isEmpty();
- }
- }
-
- public boolean validateSize() {
- boolean notNullOrEmpty = validateNotNullOrEmpty();
- if (this.prefixed) {
- return notNullOrEmpty && !(this.prefix.length() < prefixMinLength || this.secret.length() < secretMinLength ||
- this.salt.length() < saltMinLength);
- } else {
- return notNullOrEmpty && !(this.secret.length() < secretMinLength || this.salt.length() < saltMinLength);
- }
+ super(secret, salt);
}
}
diff --git a/hopsworks-common/src/main/java/io/hops/hopsworks/common/user/AuthController.java b/hopsworks-common/src/main/java/io/hops/hopsworks/common/user/AuthController.java
index 27d0b03f14..711ebce73a 100644
--- a/hopsworks-common/src/main/java/io/hops/hopsworks/common/user/AuthController.java
+++ b/hopsworks-common/src/main/java/io/hops/hopsworks/common/user/AuthController.java
@@ -40,6 +40,7 @@
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings;
+import io.hops.hopsworks.api.auth.UserStatusValidator;
import io.hops.hopsworks.common.dao.certificates.CertsFacade;
import io.hops.hopsworks.persistence.entity.certificates.UserCerts;
import io.hops.hopsworks.persistence.entity.project.Project;
diff --git a/hopsworks-common/src/main/java/io/hops/hopsworks/common/user/UsersController.java b/hopsworks-common/src/main/java/io/hops/hopsworks/common/user/UsersController.java
index 89780ba25f..6c2647d187 100644
--- a/hopsworks-common/src/main/java/io/hops/hopsworks/common/user/UsersController.java
+++ b/hopsworks-common/src/main/java/io/hops/hopsworks/common/user/UsersController.java
@@ -41,6 +41,7 @@
import com.google.common.base.Strings;
import com.google.zxing.WriterException;
+import io.hops.hopsworks.api.auth.UserStatusValidator;
import io.hops.hopsworks.common.dao.project.ProjectFacade;
import io.hops.hopsworks.common.dao.project.team.ProjectTeamFacade;
import io.hops.hopsworks.common.dao.user.UsersDTO;
@@ -80,7 +81,6 @@
import java.io.IOException;
import java.sql.Timestamp;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Optional;
@@ -595,15 +595,6 @@ public boolean isUserInRole(Users user, String groupName) {
return user.getBbcGroupCollection().contains(group);
}
- public List getUserRoles(Users p) {
- Collection groupList = p.getBbcGroupCollection();
- List list = new ArrayList<>();
- for (BbcGroup g : groupList) {
- list.add(g.getGroupName());
- }
- return list;
- }
-
/**
* Delete users. Will fail if the user is an initiator of an audit log.
* @param u
diff --git a/hopsworks-common/src/main/java/io/hops/hopsworks/common/user/security/apiKey/ApiKeyController.java b/hopsworks-common/src/main/java/io/hops/hopsworks/common/user/security/apiKey/ApiKeyController.java
index 336d4798f2..708c3aa20e 100644
--- a/hopsworks-common/src/main/java/io/hops/hopsworks/common/user/security/apiKey/ApiKeyController.java
+++ b/hopsworks-common/src/main/java/io/hops/hopsworks/common/user/security/apiKey/ApiKeyController.java
@@ -15,10 +15,10 @@
*/
package io.hops.hopsworks.common.user.security.apiKey;
+import io.hops.hopsworks.api.auth.key.ApiKeyFacade;
+import io.hops.hopsworks.api.auth.key.ApiKeyScopeFacade;
import io.hops.hopsworks.persistence.entity.user.security.apiKey.ApiKey;
-import io.hops.hopsworks.common.dao.user.security.apiKey.ApiKeyFacade;
import io.hops.hopsworks.persistence.entity.user.security.apiKey.ApiKeyScope;
-import io.hops.hopsworks.common.dao.user.security.apiKey.ApiKeyScopeFacade;
import io.hops.hopsworks.persistence.entity.user.security.apiKey.ApiScope;
import io.hops.hopsworks.persistence.entity.user.Users;
import io.hops.hopsworks.common.dao.user.security.ua.UserAccountsEmailMessages;
@@ -131,42 +131,6 @@ private List getKeyScopes(Set scopes, ApiKey apiKey) {
return keyScopes;
}
- /**
- *
- * @param apiKey
- * @return
- */
- public Set getScopes(ApiKey apiKey) {
- Set scopes = new HashSet<>();
- for (ApiKeyScope scope : apiKey.getApiKeyScopeCollection()) {
- scopes.add(scope.getScope());
- }
- return scopes;
- }
-
- /**
- *
- * @param key
- * @return
- * @throws ApiKeyException
- */
- public ApiKey getApiKey(String key) throws ApiKeyException {
- String[] parts = key.split(Secret.KEY_ID_SEPARATOR_REGEX);
- if (parts.length < 2) {
- throw new ApiKeyException(RESTCodes.ApiKeyErrorCode.KEY_INVALID, Level.FINE);
- }
- ApiKey apiKey = apiKeyFacade.findByPrefix(parts[0]);
- if (apiKey == null) {
- throw new ApiKeyException(RESTCodes.ApiKeyErrorCode.KEY_NOT_FOUND_IN_DATABASE, Level.FINE);
- }
- //___MinLength can be set to 0 b/c no validation is needed if the key was in db
- Secret secret = new Secret(parts[0], parts[1], apiKey.getSalt());
- if (!secret.getSha256HexDigest().equals(apiKey.getSecret())) {
- throw new ApiKeyException(RESTCodes.ApiKeyErrorCode.KEY_INVALID, Level.FINE);
- }
- return apiKey;
- }
-
/**
*
* @param user
diff --git a/hopsworks-common/src/main/java/io/hops/hopsworks/common/util/Settings.java b/hopsworks-common/src/main/java/io/hops/hopsworks/common/util/Settings.java
index 217a4129a7..c07771be93 100644
--- a/hopsworks-common/src/main/java/io/hops/hopsworks/common/util/Settings.java
+++ b/hopsworks-common/src/main/java/io/hops/hopsworks/common/util/Settings.java
@@ -300,9 +300,9 @@ public class Settings implements Serializable {
private static final String VARIABLE_JWT_SIGNING_KEY_NAME = "jwt_signing_key_name";
private static final String VARIABLE_JWT_ISSUER_KEY = "jwt_issuer";
- private static final String VARIABLE_SERVICE_MASTER_JWT = "service_master_jwt";
private static final String VARIABLE_SERVICE_JWT_LIFETIME_MS = "service_jwt_lifetime_ms";
private static final String VARIABLE_SERVICE_JWT_EXP_LEEWAY_SEC = "service_jwt_exp_leeway_sec";
+ private static final String VARIABLE_SERVICE_API_KEY = "int_service_api_key";
private static final String VARIABLE_CONNECTION_KEEPALIVE_TIMEOUT = "keepalive_timeout";
@@ -322,7 +322,7 @@ public class Settings implements Serializable {
private static final String VARIABLE_FS_JAVA_JOB_UTIL_PATH = "fs_java_job_util";
private static final String VARIABLE_HDFS_FILE_OP_JOB_UTIL = "hdfs_file_op_job_util";
private static final String VARIABLE_HDFS_FILE_OP_JOB_DRIVER_MEM = "hdfs_file_op_job_driver_mem";
-
+
// Storage connectors
private final static String VARIABLE_ENABLE_REDSHIFT_STORAGE_CONNECTORS = "enable_redshift_storage_connectors";
@@ -447,7 +447,7 @@ static KubeType fromString(String str){
private static final String VARIABLE_DOCKER_NAMESPACE = "docker_namespace";
private static final String VARIABLE_MANAGED_DOCKER_REGISTRY =
"managed_docker_registry";
-
+
private String setVar(String varName, String defaultValue) {
return setStrVar(varName, defaultValue);
}
@@ -795,7 +795,7 @@ private void populateCache() {
KUBE_TAINTED_NODES = setStrVar(VARIABLE_KUBE_TAINTED_NODES, KUBE_TAINTED_NODES);
KUBE_TAINTED_NODES_MONITOR_INTERVAL = setStrVar(VARIABLE_KUBE_TAINTED_NODES_MONITOR_INTERVAL,
KUBE_TAINTED_NODES_MONITOR_INTERVAL);
-
+
HOPSWORKS_ENTERPRISE = setBoolVar(VARIABLE_HOPSWORKS_ENTERPRISE, HOPSWORKS_ENTERPRISE);
JUPYTER_HOST = setStrVar(VARIABLE_JUPYTER_HOST, JUPYTER_HOST);
@@ -809,8 +809,6 @@ private void populateCache() {
SERVICE_JWT_LIFETIME_MS = setLongVar(VARIABLE_SERVICE_JWT_LIFETIME_MS, SERVICE_JWT_LIFETIME_MS);
SERVICE_JWT_EXP_LEEWAY_SEC = setIntVar(VARIABLE_SERVICE_JWT_EXP_LEEWAY_SEC, SERVICE_JWT_EXP_LEEWAY_SEC);
- populateServiceJWTCache();
-
CONNECTION_KEEPALIVE_TIMEOUT = setIntVar(VARIABLE_CONNECTION_KEEPALIVE_TIMEOUT, CONNECTION_KEEPALIVE_TIMEOUT);
FEATURESTORE_DB_DEFAULT_QUOTA = setLongVar(VARIABLE_FEATURESTORE_DEFAULT_QUOTA, FEATURESTORE_DB_DEFAULT_QUOTA);
@@ -914,7 +912,7 @@ private void populateCache() {
DOCKER_CGROUP_PARENT = setStrVar(VARIABLE_DOCKER_CGROUP_PARENT, DOCKER_CGROUP_PARENT);
PROMETHEUS_PORT = setIntVar(VARIABLE_PROMETHEUS_PORT, PROMETHEUS_PORT);
-
+
SKIP_NAMESPACE_CREATION = setBoolVar(VARIABLE_SKIP_NAMESPACE_CREATION,
SKIP_NAMESPACE_CREATION);
@@ -931,7 +929,7 @@ private void populateCache() {
QUOTAS_MAX_PARALLEL_EXECUTIONS);
QUOTAS_MAX_PARALLEL_EXECUTIONS = setLongVar(VARIABLE_QUOTAS_MAX_PARALLEL_EXECUTIONS,
QUOTAS_MAX_PARALLEL_EXECUTIONS);
-
+
SQL_MAX_SELECT_IN = setIntVar(VARIABLE_SQL_MAX_SELECT_IN, SQL_MAX_SELECT_IN);
ENABLE_JUPYTER_PYTHON_KERNEL_NON_KUBERNETES = setBoolVar(VARIABLE_ENABLE_JUPYTER_PYTHON_KERNEL_NON_KUBERNETES,
@@ -950,6 +948,8 @@ private void populateCache() {
COMMAND_SEARCH_FS_HISTORY_CLEAN_PERIOD);
COMMAND_SEARCH_FS_RETRY_PER_CLEAN_INTERVAL = setIntVar(VARIABLE_COMMAND_SEARCH_FS_RETRY_PER_CLEAN_INTERVAL,
COMMAND_SEARCH_FS_RETRY_PER_CLEAN_INTERVAL);
+ SERVICE_API_KEY = setVar(VARIABLE_SERVICE_API_KEY, SERVICE_API_KEY);
+
OPENSEARCH_DEFAULT_EMBEDDING_INDEX_NAME = setStrVar(
VARIABLE_OPENSEARCH_DEFAULT_EMBEDDING_INDEX, OPENSEARCH_DEFAULT_EMBEDDING_INDEX_NAME);
OPENSEARCH_NUM_DEFAULT_EMBEDDING_INDEX = setIntVar(
@@ -2332,17 +2332,7 @@ public synchronized Boolean isDelaEnabled() {
checkCache();
return DELA_ENABLED;
}
-
- private void populateServiceJWTCache() {
- SERVICE_MASTER_JWT = setStrVar(VARIABLE_SERVICE_MASTER_JWT, SERVICE_MASTER_JWT);
- RENEW_TOKENS = new String[NUM_OF_SERVICE_RENEW_TOKENS];
- for (int i = 0; i < NUM_OF_SERVICE_RENEW_TOKENS; i++) {
- String variableKey = String.format(SERVICE_RENEW_TOKEN_VARIABLE_TEMPLATE, i);
- String token = setStrVar(variableKey, "");
- RENEW_TOKENS[i] = token;
- }
- }
-
+
//************************************************ZOOKEEPER********************************************************
public static final int ZOOKEEPER_SESSION_TIMEOUT_MS = 30 * 1000;//30 seconds
//Zookeeper END
@@ -2380,7 +2370,7 @@ private void populateServiceJWTCache() {
private static final String VARIABLE_OAUTH_LOGOUT_REDIRECT_URI = "oauth_logout_redirect_uri";
private static final String VARIABLE_OAUTH_ACCOUNT_STATUS = "oauth_account_status";
private static final String VARIABLE_OAUTH_GROUP_MAPPING = "oauth_group_mapping";
-
+
private static final String VARIABLE_REMOTE_AUTH_NEED_CONSENT = "remote_auth_need_consent";
private static final String VARIABLE_DISABLE_PASSWORD_LOGIN = "disable_password_login";
@@ -2464,7 +2454,7 @@ private void populateLDAPCache() {
OAUTH_LOGOUT_REDIRECT_URI = setStrVar(VARIABLE_OAUTH_LOGOUT_REDIRECT_URI, OAUTH_LOGOUT_REDIRECT_URI);
OAUTH_ACCOUNT_STATUS = setIntVar(VARIABLE_OAUTH_ACCOUNT_STATUS, OAUTH_ACCOUNT_STATUS);
OAUTH_GROUP_MAPPING = setStrVar(VARIABLE_OAUTH_GROUP_MAPPING, OAUTH_GROUP_MAPPING);
-
+
REMOTE_AUTH_NEED_CONSENT = setBoolVar(VARIABLE_REMOTE_AUTH_NEED_CONSENT, REMOTE_AUTH_NEED_CONSENT);
DISABLE_PASSWORD_LOGIN = setBoolVar(VARIABLE_DISABLE_PASSWORD_LOGIN, DISABLE_PASSWORD_LOGIN);
@@ -2473,7 +2463,7 @@ private void populateLDAPCache() {
LDAP_GROUP_MAPPING_SYNC_INTERVAL = setLongVar(VARIABLE_LDAP_GROUP_MAPPING_SYNC_INTERVAL,
LDAP_GROUP_MAPPING_SYNC_INTERVAL);
-
+
VALIDATE_REMOTE_USER_EMAIL_VERIFIED =
setBoolVar(VARIABLE_VALIDATE_REMOTE_USER_EMAIL_VERIFIED, VALIDATE_REMOTE_USER_EMAIL_VERIFIED);
@@ -2595,7 +2585,7 @@ public synchronized String getOAuthGroupMapping() {
checkCache();
return OAUTH_GROUP_MAPPING;
}
-
+
public void updateOAuthGroupMapping(String mapping) {
updateVariableInternal(VARIABLE_OAUTH_GROUP_MAPPING, mapping, VariablesVisibility.ADMIN);
}
@@ -3267,32 +3257,10 @@ public synchronized String getJWTIssuer() {
return JWT_ISSUER;
}
- private String SERVICE_MASTER_JWT = "";
- public synchronized String getServiceMasterJWT() {
+ private String SERVICE_API_KEY = "";
+ public synchronized String getServiceApiKey() {
checkCache();
- return SERVICE_MASTER_JWT;
- }
-
- public synchronized void setServiceMasterJWT(String JWT) {
- updateVariableInternal(VARIABLE_SERVICE_MASTER_JWT, JWT, VariablesVisibility.ADMIN);
- em.flush();
- SERVICE_MASTER_JWT = JWT;
- }
-
- private final int NUM_OF_SERVICE_RENEW_TOKENS = 5;
- private final static String SERVICE_RENEW_TOKEN_VARIABLE_TEMPLATE = "service_renew_token_%d";
- private String[] RENEW_TOKENS = new String[0];
- public synchronized String[] getServiceRenewJWTs() {
- checkCache();
- return RENEW_TOKENS;
- }
-
- public synchronized void setServiceRenewJWTs(String[] renewTokens) {
- for (int i = 0; i < renewTokens.length; i++) {
- String variableKey = String.format(SERVICE_RENEW_TOKEN_VARIABLE_TEMPLATE, i);
- updateVariableInternal(variableKey, renewTokens[i], VariablesVisibility.ADMIN);
- }
- RENEW_TOKENS = renewTokens;
+ return SERVICE_API_KEY;
}
private int CONNECTION_KEEPALIVE_TIMEOUT = 30;
diff --git a/hopsworks-jwt/src/main/java/io/hops/hopsworks/jwt/JWTController.java b/hopsworks-jwt/src/main/java/io/hops/hopsworks/jwt/JWTController.java
index a40f863715..09e43d5184 100644
--- a/hopsworks-jwt/src/main/java/io/hops/hopsworks/jwt/JWTController.java
+++ b/hopsworks-jwt/src/main/java/io/hops/hopsworks/jwt/JWTController.java
@@ -26,13 +26,11 @@
import io.hops.hopsworks.jwt.exception.AccessException;
import io.hops.hopsworks.jwt.exception.DuplicateSigningKeyException;
import io.hops.hopsworks.jwt.exception.InvalidationException;
-import io.hops.hopsworks.jwt.exception.JWTException;
import io.hops.hopsworks.jwt.exception.NotRenewableException;
import io.hops.hopsworks.jwt.exception.SigningKeyNotFoundException;
import io.hops.hopsworks.jwt.exception.VerificationException;
import io.hops.hopsworks.persistence.entity.jwt.InvalidJwt;
import io.hops.hopsworks.persistence.entity.jwt.JwtSigningKey;
-import org.apache.commons.lang3.tuple.Pair;
import javax.ejb.EJB;
import javax.ejb.Stateless;
@@ -465,69 +463,6 @@ public String renewToken(String token, Date newExp, Date notBefore, boolean inva
return renewedToken;
}
- public Pair renewServiceToken(String oneTimeRenewalToken, String serviceToken, Date newExpiration,
- Date newNotBefore, Long serviceJWTLifetimeMS, String username, List userRoles,
- List audience, String remoteHostname, String issuer, String defaultJWTSigningKeyName, boolean force)
- throws JWTException, NoSuchAlgorithmException {
- Map claims = new HashMap<>(4);
- claims.put(Constants.RENEWABLE, false);
- claims.put(Constants.EXPIRY_LEEWAY, 3600);
- claims.put(Constants.ROLES, userRoles.toArray(new String[1]));
- String renewalKeyName = getServiceOneTimeJWTSigningKeyname(username, remoteHostname);
- LocalDateTime masterExpiration = newExpiration.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
- LocalDateTime notBefore = computeNotBefore4ServiceRenewalTokens(masterExpiration);
- LocalDateTime expiresAt = notBefore.plus(serviceJWTLifetimeMS, ChronoUnit.MILLIS);
- JsonWebToken jwtSpecs = new JsonWebToken();
- jwtSpecs.setSubject(username);
- jwtSpecs.setIssuer(issuer);
- jwtSpecs.setAudience(audience);
- jwtSpecs.setKeyId(renewalKeyName);
- jwtSpecs.setNotBefore(localDateTime2Date(notBefore));
- jwtSpecs.setExpiresAt(localDateTime2Date(expiresAt));
- try {
- // Then generate the new one-time tokens
- String[] renewalTokens = generateOneTimeTokens4ServiceJWTRenewal(jwtSpecs, claims, defaultJWTSigningKeyName);
-
- String signingKeyId = getSignKeyID(renewalTokens[0]);
- DecodedJWT serviceJWT = decodeToken(serviceToken);
- claims.clear();
- claims.put(Constants.RENEWABLE, false);
- claims.put(Constants.SERVICE_JWT_RENEWAL_KEY_ID, signingKeyId);
- claims.put(Constants.EXPIRY_LEEWAY, getExpLeewayClaim(serviceJWT));
-
- // Finally renew the service master token
- String renewedServiceToken = renewToken(serviceToken, newExpiration, newNotBefore, false, claims, force);
- invalidate(oneTimeRenewalToken);
- return Pair.of(renewedServiceToken, renewalTokens);
- } catch (JWTException | NoSuchAlgorithmException ex) {
- if (renewalKeyName != null) {
- deleteSigningKey(renewalKeyName);
- }
- throw ex;
- }
- }
-
- public void invalidateServiceToken(String serviceToken2invalidate, String defaultJWTSigningKeyName) {
- DecodedJWT serviceJWT2invalidate = decodeToken(serviceToken2invalidate);
- try {
- invalidate(serviceToken2invalidate);
- } catch (InvalidationException ex) {
- LOGGER.log(Level.WARNING, "Could not invalidate service JWT with ID " + serviceJWT2invalidate.getId()
- + ". Continuing with deleting signing key");
- }
- Claim signingKeyID = serviceJWT2invalidate.getClaim(Constants.SERVICE_JWT_RENEWAL_KEY_ID);
- if (signingKeyID != null && !signingKeyID.isNull()) {
- // Do not use Claim.asInt, it returns null
- JwtSigningKey signingKey = findSigningKeyById(Integer.parseInt(signingKeyID.asString()));
- if (signingKey != null && defaultJWTSigningKeyName != null) {
- if (!defaultJWTSigningKeyName.equals(signingKey.getName())
- && !ONE_TIME_JWT_SIGNING_KEY_NAME.equals(signingKey.getName())) {
- deleteSigningKey(signingKey.getName());
- }
- }
- }
- }
-
public String getSignKeyID(String token) {
DecodedJWT jwt = decodeToken(token);
return jwt.getKeyId();
diff --git a/hopsworks-rest-utils/src/main/java/io/hops/hopsworks/exceptions/InvalidQueryException.java b/hopsworks-persistence/src/main/java/io/hops/hopsworks/persistence/InvalidQueryException.java
similarity index 96%
rename from hopsworks-rest-utils/src/main/java/io/hops/hopsworks/exceptions/InvalidQueryException.java
rename to hopsworks-persistence/src/main/java/io/hops/hopsworks/persistence/InvalidQueryException.java
index 59082379f4..bd80410453 100644
--- a/hopsworks-rest-utils/src/main/java/io/hops/hopsworks/exceptions/InvalidQueryException.java
+++ b/hopsworks-persistence/src/main/java/io/hops/hopsworks/persistence/InvalidQueryException.java
@@ -13,13 +13,12 @@
* You should have received a copy of the GNU Affero General Public License along with this program.
* If not, see .
*/
-package io.hops.hopsworks.exceptions;
+package io.hops.hopsworks.persistence;
import javax.ejb.ApplicationException;
@ApplicationException
public class InvalidQueryException extends IllegalArgumentException {
-
public InvalidQueryException() {
}
@@ -34,5 +33,4 @@ public InvalidQueryException(String message, Throwable cause) {
public InvalidQueryException(Throwable cause) {
super(cause);
}
-
}
diff --git a/hopsworks-persistence/src/main/java/io/hops/hopsworks/persistence/entity/user/security/apiKey/ApiScope.java b/hopsworks-persistence/src/main/java/io/hops/hopsworks/persistence/entity/user/security/apiKey/ApiScope.java
index 0b15b5f0a3..a1b7eb18b8 100644
--- a/hopsworks-persistence/src/main/java/io/hops/hopsworks/persistence/entity/user/security/apiKey/ApiScope.java
+++ b/hopsworks-persistence/src/main/java/io/hops/hopsworks/persistence/entity/user/security/apiKey/ApiScope.java
@@ -15,6 +15,8 @@
*/
package io.hops.hopsworks.persistence.entity.user.security.apiKey;
+import com.google.common.collect.Sets;
+
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
@@ -35,7 +37,8 @@ public enum ApiScope {
MODELREGISTRY(false, true),
USER(false, true),
GIT(false, false),
- PYTHON_LIBRARIES(false, false);
+ PYTHON_LIBRARIES(false, false),
+ AUTH(true, false);
private final boolean privileged;
private final boolean saas;// Hopsworks as a service user scopes
@@ -70,4 +73,11 @@ public static Set getServiceUserScopes() {
.filter(as -> as.saas)
.collect(Collectors.toSet());
}
+
+ private static final Set AGENT_SCOPES = Sets.newHashSet(AUTH);
+ public static Set getAgentUserScopes() {
+ Set unprivileged = getUnprivileged();
+ unprivileged.addAll(AGENT_SCOPES);
+ return unprivileged;
+ }
}
\ No newline at end of file
diff --git a/hopsworks-persistence/src/main/java/io/hops/hopsworks/persistence/entity/util/AbstractFacade.java b/hopsworks-persistence/src/main/java/io/hops/hopsworks/persistence/entity/util/AbstractFacade.java
new file mode 100644
index 0000000000..b192697206
--- /dev/null
+++ b/hopsworks-persistence/src/main/java/io/hops/hopsworks/persistence/entity/util/AbstractFacade.java
@@ -0,0 +1,301 @@
+/*
+ * This file is part of Hopsworks
+ * Copyright (C) 2023, Hopsworks AB. All rights reserved
+ *
+ * Hopsworks is free software: you can redistribute it and/or modify it under the terms of
+ * the GNU Affero General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * Hopsworks 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License along with this program.
+ * If not, see .
+ */
+package io.hops.hopsworks.persistence.entity.util;
+
+import io.hops.hopsworks.persistence.InvalidQueryException;
+
+import javax.persistence.EntityManager;
+import javax.persistence.Query;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+public abstract class AbstractFacade {
+
+ public static Integer BATCH_SIZE = 100;
+
+ protected final Class entityClass;
+
+ public AbstractFacade(Class entityClass) {
+ this.entityClass = entityClass;
+ }
+
+ protected abstract EntityManager getEntityManager();
+
+ public void save(T entity) {
+ getEntityManager().persist(entity);
+ }
+
+ public T update(T entity) {
+ return getEntityManager().merge(entity);
+ }
+
+ public void remove(T entity) {
+ if (entity == null) {
+ return;
+ }
+ getEntityManager().remove(getEntityManager().merge(entity));
+ getEntityManager().flush();
+ }
+
+ public void removeBatch(List entities) {
+ for (T item : entities) {
+ getEntityManager().remove(getEntityManager().merge(item));
+ }
+
+ getEntityManager().flush();
+ }
+
+ public T find(Object id) {
+ return getEntityManager().find(entityClass, id);
+ }
+
+ public List findAll() {
+ javax.persistence.criteria.CriteriaQuery cq = getEntityManager().
+ getCriteriaBuilder().createQuery();
+ cq.select(cq.from(entityClass));
+ return getEntityManager().createQuery(cq).getResultList();
+ }
+
+ public List findRange(int[] range) {
+ javax.persistence.criteria.CriteriaQuery cq = getEntityManager().
+ getCriteriaBuilder().createQuery();
+ cq.select(cq.from(entityClass));
+ javax.persistence.Query q = getEntityManager().createQuery(cq);
+ q.setMaxResults(range[1] - range[0]);
+ q.setFirstResult(range[0]);
+ return q.getResultList();
+ }
+
+ public long count() {
+ javax.persistence.criteria.CriteriaQuery cq = getEntityManager().
+ getCriteriaBuilder().createQuery();
+ javax.persistence.criteria.Root rt = cq.from(entityClass);
+ cq.select(getEntityManager().getCriteriaBuilder().count(rt)).where();
+ javax.persistence.Query q = getEntityManager().createQuery(cq);
+ return (Long) q.getSingleResult();
+ }
+
+ public void setOffsetAndLim(Integer offset, Integer limit, Query q) {
+ if (offset != null && offset > 0) {
+ q.setFirstResult(offset);
+ }
+ if (limit != null && limit > 0) {
+ q.setMaxResults(limit);
+ }
+ }
+
+ public String OrderBy(SortBy sortBy) {
+ return sortBy.getSql() + " " + sortBy.getParam().getSql();
+ }
+
+ public String buildQuery(String query, Set extends FilterBy> filters,
+ Set extends AbstractFacade.SortBy> sorts, String more) {
+ return query + buildFilterString(filters, more) + buildSortString(sorts);
+ }
+
+ public String buildSortString(Set extends SortBy> sortBy) {
+ if (sortBy == null || sortBy.isEmpty()) {
+ return "";
+ }
+ sortBy.remove(null);
+ Iterator extends SortBy> sort = sortBy.iterator();
+ if (!sort.hasNext()) {
+ return "";
+ }
+ StringBuilder c = new StringBuilder(" ORDER BY " + OrderBy(sort.next()));
+ for (;sort.hasNext();) {
+ c.append(", ").append(OrderBy(sort.next()));
+ }
+ return c.toString();
+ }
+
+ public String buildFilterString(Set extends FilterBy> filter, String more) {
+ String s = more == null || more.isEmpty() ? "" : "WHERE " + more;
+ if (filter == null || filter.isEmpty()) {
+ return s;
+ }
+ filter.remove(null);
+ Iterator extends FilterBy> filterBy = filter.iterator();
+ if (!filterBy.hasNext()) {
+ return s;
+ }
+ StringBuilder c = new StringBuilder(" WHERE " + filterBy.next().getSql());
+ for (;filterBy.hasNext();) {
+ c.append(" AND ").append(filterBy.next().getSql());
+ }
+ return c.append(more == null || more.isEmpty()? "": " AND " + more).toString();
+ }
+
+ public Date getDate(String field, String value) {
+ String[] formats = {"yyyy-MM-dd'T'HH:mm:ss.SSSX", "yyyy-MM-dd'T'HH:mm:ss.SSSZ", "yyyy-MM-dd'T'HH:mm:ssX",
+ "yyyy-MM-dd'T'HH:mm:ssZ", "yyyy-MM-dd'T'HH:mm:sss", "yyyy-MM-dd"};
+ Date date = null;
+ for (int i = 0; i < formats.length && date == null; i++ ) {
+ date = getDateByFormat(value, formats[i]);
+ }
+ if (date == null) {
+ throw new InvalidQueryException(
+ "Filter value for " + field + " needs to set valid format. Expected:yyyy-MM-dd hh:mm:ss.SSSX but found: " +
+ value);
+ }
+ return date;
+ }
+
+ private Date getDateByFormat(String value, String format) {
+ SimpleDateFormat sdf = new SimpleDateFormat(format);
+ try {
+ return sdf.parse(value);
+ } catch (ParseException e) {
+ return null;
+ }
+ }
+
+ public Integer getIntValue(FilterBy filterBy) {
+ return getIntValue(filterBy.getField(), filterBy.getParam());
+ }
+
+ public Integer getIntValue(String field, String value) {
+ Integer val;
+ try {
+ val = Integer.parseInt(value);
+ } catch (NumberFormatException e) {
+ throw new InvalidQueryException("Filter value for " + field + " needs to set an Integer, but found: " + value);
+ }
+ return val;
+ }
+
+ public Long getLongValue(String field, String value) {
+ try {
+ return Long.parseLong(value);
+ } catch (NumberFormatException e) {
+ throw new InvalidQueryException("Filter value for " + field + " needs to set a Long, but found: " + value);
+ }
+ }
+
+ public List getIntValues(FilterBy filterBy) {
+ String[] filterStrs = splitFilterParams(filterBy);
+ List values = new ArrayList<>();
+ String field = filterBy.getField();
+ Integer val;
+ for (String filterStr : filterStrs) {
+ val = getIntValue(field, filterStr);
+ values.add(val);
+ }
+ return values;
+ }
+
+ public boolean getBooleanValue(String value) {
+ return "1".equals(value) || "true".equalsIgnoreCase(value);
+ }
+
+ public > List getEnumValues(FilterBy filterBy, final Class enumType) {
+ String[] filterStrs = splitFilterParams(filterBy);
+ List enumObjects = new ArrayList<>();
+ String field = filterBy.getField();
+ E enumObject;
+ for (String filterStr : filterStrs) {
+ enumObject = getEnumValue(field, filterStr, enumType);
+ enumObjects.add(enumObject);
+ }
+ return enumObjects;
+ }
+
+ public > E getEnumValue(String field, String filterStr, final Class enumType) {
+ E enumObject;
+ try {
+ enumObject = E.valueOf(enumType, filterStr);
+ } catch (IllegalArgumentException iae) {
+ throw new InvalidQueryException("Filter value for " + field + " needs to set valid " + field + ", but found: "
+ + filterStr, iae);
+ }
+ return enumObject;
+ }
+
+ public String[] splitFilterParams(FilterBy filterBy) {
+ return filterBy.getParam().split(",");
+ }
+
+ public interface SortBy {
+ String getValue();
+ OrderBy getParam();
+ String getSql();
+ }
+
+ public interface FilterBy {
+ String getValue();
+ String getParam();
+ String getSql();
+ String getField();
+ }
+
+ public enum OrderBy {
+ ASC ("ASC", "ASC"),
+ DESC ("DESC", "DESC");
+
+ private final String value;
+ private final String sql;
+
+ private OrderBy(String value, String sql) {
+ this.value = value;
+ this.sql = sql;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public String getSql() {
+ return sql;
+ }
+
+ @Override
+ public String toString() {
+ return value;
+ }
+
+ }
+
+ public static class CollectionInfo {
+ private Long count;
+ private List items;
+
+ public CollectionInfo(Long count, List items) {
+ this.count = count;
+ this.items = items;
+ }
+
+ public Long getCount() {
+ return count;
+ }
+
+ public List getItems() {
+ return items;
+ }
+
+ public void setItems(List items) {
+ this.items = items;
+ }
+
+ public void setCount(Long count) {
+ this.count = count;
+ }
+ }
+}
diff --git a/hopsworks-rest-utils/pom.xml b/hopsworks-rest-utils/pom.xml
index 3c37a97de4..52030c79b8 100644
--- a/hopsworks-rest-utils/pom.xml
+++ b/hopsworks-rest-utils/pom.xml
@@ -52,6 +52,10 @@
io.hops
hadoop-client-api
+
+ io.hops.hopsworks
+ hopsworks-persistence
+
com.fasterxml.jackson.datatype
diff --git a/hopsworks-rest-utils/src/main/java/io/hops/hopsworks/restutils/RESTApiJsonResponse.java b/hopsworks-rest-utils/src/main/java/io/hops/hopsworks/restutils/RESTApiJsonResponse.java
new file mode 100644
index 0000000000..6ab3325e40
--- /dev/null
+++ b/hopsworks-rest-utils/src/main/java/io/hops/hopsworks/restutils/RESTApiJsonResponse.java
@@ -0,0 +1,67 @@
+/*
+ * This file is part of Hopsworks
+ * Copyright (C) 2023, Hopsworks AB. All rights reserved
+ *
+ * Hopsworks is free software: you can redistribute it and/or modify it under the terms of
+ * the GNU Affero General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * Hopsworks 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License along with this program.
+ * If not, see .
+ */
+package io.hops.hopsworks.restutils;
+
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+import java.util.List;
+
+@XmlRootElement
+public class RESTApiJsonResponse extends JsonResponse {
+
+ private String QRCode;
+ private List fieldErrors;
+ private Object data;
+ private String sessionID;
+
+ public RESTApiJsonResponse() {
+ }
+
+
+ public List getFieldErrors() {
+ return fieldErrors;
+ }
+
+ public void setFieldErrors(List fieldErrors) {
+ this.fieldErrors = fieldErrors;
+ }
+
+ public Object getData() {
+ return data;
+ }
+
+ public void setData(Object data) {
+ this.data = data;
+ }
+
+ @XmlElement
+ public String getSessionID() {
+ return sessionID;
+ }
+
+ public void setSessionID(String sessionID) {
+ this.sessionID = sessionID;
+ }
+
+ public String getQRCode() {
+ return QRCode;
+ }
+
+ public void setQRCode(String QRCode) {
+ this.QRCode = QRCode;
+ }
+
+}
diff --git a/hopsworks-rest-utils/src/main/java/io/hops/hopsworks/restutils/ThrowableMapper.java b/hopsworks-rest-utils/src/main/java/io/hops/hopsworks/restutils/ThrowableMapper.java
index d41c95829a..7d38fca5e6 100644
--- a/hopsworks-rest-utils/src/main/java/io/hops/hopsworks/restutils/ThrowableMapper.java
+++ b/hopsworks-rest-utils/src/main/java/io/hops/hopsworks/restutils/ThrowableMapper.java
@@ -18,10 +18,10 @@
import com.google.common.base.Strings;
import io.hops.hopsworks.exceptions.GenericException;
import io.hops.hopsworks.exceptions.HopsSecurityException;
-import io.hops.hopsworks.exceptions.InvalidQueryException;
import io.hops.hopsworks.exceptions.ResourceException;
import io.hops.hopsworks.exceptions.ServiceException;
import io.hops.hopsworks.exceptions.UserException;
+import io.hops.hopsworks.persistence.InvalidQueryException;
import org.apache.hadoop.security.AccessControlException;
import javax.ejb.AccessLocalException;
import javax.ejb.EJBException;
diff --git a/pom.xml b/pom.xml
index 1ad25e9cbc..ad4b5e24fe 100644
--- a/pom.xml
+++ b/pom.xml
@@ -60,6 +60,7 @@
alerting
hopsworks-alert
hopsworks-service-discovery
+ hopsworks-api-auth
vector-db
@@ -584,6 +585,12 @@
hopsworks-service-discovery
${project.version}
+
+ io.hops.hopsworks
+ hopsworks-api-auth
+ ${project.version}
+ ejb
+
io.hops.metadata