Implemented part of OAuth2Service

This commit is contained in:
viktor 2020-06-23 14:38:50 +03:00
parent c3c889bbbf
commit 5a00973b47
2 changed files with 267 additions and 34 deletions

View File

@ -20,6 +20,7 @@ import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo;
import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistration;
import org.thingsboard.server.common.data.oauth2.OAuth2ClientsParams;
import java.util.List;
@ -28,13 +29,13 @@ public interface OAuth2Service {
List<OAuth2ClientInfo> getOAuth2Clients(String domainName);
List<OAuth2ClientRegistration> getSystemOAuth2ClientRegistrations(TenantId tenantId);
OAuth2ClientsParams saveSystemOAuth2ClientsParams(OAuth2ClientsParams oAuth2ClientsParams);
List<OAuth2ClientRegistration> getTenantOAuth2ClientRegistrations(TenantId tenantId);
OAuth2ClientsParams saveTenantOAuth2ClientsParams(TenantId tenantId, OAuth2ClientsParams oAuth2ClientsParams);
OAuth2ClientRegistration saveSystemOAuth2ClientRegistration(OAuth2ClientRegistration clientRegistration);
OAuth2ClientsParams getSystemOAuth2ClientsParams(TenantId tenantId);
OAuth2ClientRegistration saveTenantOAuth2ClientRegistration(TenantId tenantId, String domainName, OAuth2ClientRegistration clientRegistration);
OAuth2ClientsParams getTenantOAuth2ClientsParams(TenantId tenantId);
void deleteDomainOAuth2ClientRegistrationByTenant(TenantId tenantId);

View File

@ -16,24 +16,35 @@
package org.thingsboard.server.dao.oauth2;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.thingsboard.server.common.data.AdminSettings;
import org.thingsboard.server.common.data.*;
import org.thingsboard.server.common.data.id.AdminSettingsId;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.kv.AttributeKvEntry;
import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
import org.thingsboard.server.common.data.kv.StringDataEntry;
import org.thingsboard.server.common.data.oauth2.*;
import org.thingsboard.server.dao.attributes.AttributesService;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.exception.IncorrectParameterException;
import org.thingsboard.server.dao.settings.AdminSettingsService;
import org.thingsboard.server.dao.tenant.TenantService;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.function.Consumer;
@Slf4j
@Service
@ -44,65 +55,158 @@ public class OAuth2ServiceImpl implements OAuth2Service {
private static final String OAUTH2_CLIENT_REGISTRATIONS_PARAMS = "oauth2ClientRegistrationsParams";
private static final String OAUTH2_CLIENT_REGISTRATIONS_DOMAIN_NAME_PREFIX = "oauth2ClientRegistrationsDomainNamePrefix";
private static final String ALLOW_OAUTH2_CONFIGURATION = "allowOAuth2Configuration";
private static final String SYSTEM_SETTINGS_OAUTH2_VALUE = "value";
private static final String OAUTH2_AUTHORIZATION_PATH_TEMPLATE = "/oauth2/authorization/%s";
@Autowired
private AdminSettingsService adminSettingsService;
@Autowired
private AttributesService attributesService;
@Autowired
private TenantService tenantService;
@Override
public List<OAuth2ClientInfo> getOAuth2Clients(String domainName) {
return Collections.emptyList();
}
@Override
public List<OAuth2ClientRegistration> getSystemOAuth2ClientRegistrations(TenantId tenantId) {
return null;
}
@Override
public List<OAuth2ClientRegistration> getTenantOAuth2ClientRegistrations(TenantId tenantId) {
return null;
}
@Override
public OAuth2ClientRegistration saveSystemOAuth2ClientRegistration(OAuth2ClientRegistration clientRegistration) {
public OAuth2ClientsParams saveSystemOAuth2ClientsParams(OAuth2ClientsParams oAuth2ClientsParams) {
// TODO check by registration ID in entities
AdminSettings clientRegistrationParamsSettings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, OAUTH2_CLIENT_REGISTRATIONS_PARAMS);
if (clientRegistrationParamsSettings == null) {
clientRegistrationParamsSettings = new AdminSettings();
clientRegistrationParamsSettings.setKey(OAUTH2_CLIENT_REGISTRATIONS_PARAMS);
ObjectNode node = mapper.createObjectNode();
clientRegistrationParamsSettings.setJsonValue(node);
for (OAuth2ClientRegistration clientRegistration : oAuth2ClientsParams.getClientRegistrations()) {
validator.accept(clientRegistration);
}
AdminSettings clientRegistrationParamsSettings = new AdminSettings();
clientRegistrationParamsSettings.setKey(OAUTH2_CLIENT_REGISTRATIONS_PARAMS);
ObjectNode clientRegistrationsNode = mapper.createObjectNode();
oAuth2ClientsParams.setDomainName("");
String json;
try {
json = mapper.writeValueAsString(clientRegistration);
json = mapper.writeValueAsString(oAuth2ClientsParams);
} catch (JsonProcessingException e) {
log.error("Unable to convert OAuth2 Client Registration Params to JSON!", e);
throw new IncorrectParameterException("Unable to convert OAuth2 Client Registration Params to JSON!");
}
ObjectNode oldClientRegistrations = (ObjectNode) clientRegistrationParamsSettings.getJsonValue();
oldClientRegistrations.put(clientRegistration.getRegistrationId(), json);
clientRegistrationsNode.put(SYSTEM_SETTINGS_OAUTH2_VALUE, json);
clientRegistrationParamsSettings.setJsonValue(clientRegistrationsNode);
adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, clientRegistrationParamsSettings);
// TODO ask if that's worth it
return getClientRegistration(clientRegistration.getRegistrationId());
return getSystemOAuth2ClientsParams(TenantId.SYS_TENANT_ID);
}
@Override
public OAuth2ClientRegistration saveTenantOAuth2ClientRegistration(TenantId tenantId, String domainName, OAuth2ClientRegistration clientRegistration) {
public OAuth2ClientsParams saveTenantOAuth2ClientsParams(TenantId tenantId, OAuth2ClientsParams oAuth2ClientsParams) {
// TODO ask what if tenant saves config for several different domain names, do we need to check it
// TODO check by registration ID in system
return null;
for (OAuth2ClientRegistration clientRegistration : oAuth2ClientsParams.getClientRegistrations()) {
validator.accept(clientRegistration);
}
String clientRegistrationsKey = constructClientRegistrationsKey(oAuth2ClientsParams.getDomainName());
AdminSettings existentAdminSettingsByKey = adminSettingsService.findAdminSettingsByKey(tenantId, clientRegistrationsKey);
if (StringUtils.isEmpty(oAuth2ClientsParams.getAdminSettingsId())) {
if (existentAdminSettingsByKey == null) {
existentAdminSettingsByKey = saveOAuth2ClientSettings(tenantId, clientRegistrationsKey);
oAuth2ClientsParams.setAdminSettingsId(existentAdminSettingsByKey.getId().getId().toString());
} else {
log.error("Current domain name [{}] already registered in the system!", oAuth2ClientsParams.getDomainName());
throw new IncorrectParameterException("Current domain name [" + oAuth2ClientsParams.getDomainName() + "] already registered in the system!");
}
} else {
AdminSettings existentOAuth2ClientsSettingsById = adminSettingsService.findAdminSettingsById(
tenantId,
new AdminSettingsId(UUID.fromString(oAuth2ClientsParams.getAdminSettingsId()))
);
if (existentOAuth2ClientsSettingsById == null) {
log.error("Admin setting ID is already set in login white labeling object, but doesn't exist in the database");
throw new IllegalStateException("Admin setting ID is already set in login white labeling object, but doesn't exist in the database");
}
if (!existentOAuth2ClientsSettingsById.getKey().equals(clientRegistrationsKey)) {
if (existentAdminSettingsByKey == null) {
adminSettingsService.deleteAdminSettingsByKey(tenantId, existentOAuth2ClientsSettingsById.getKey());
AdminSettings newOAuth2ClientsSettings = saveOAuth2ClientSettings(tenantId, clientRegistrationsKey);
oAuth2ClientsParams.setAdminSettingsId(newOAuth2ClientsSettings.getId().getId().toString());
} else {
log.error("Current domain name [{}] already registered in the system!", oAuth2ClientsParams.getDomainName());
throw new IncorrectParameterException("Current domain name [" + oAuth2ClientsParams.getDomainName() + "] already registered in the system!");
}
}
}
String json;
try {
json = mapper.writeValueAsString(oAuth2ClientsParams);
} catch (JsonProcessingException e) {
log.error("Unable to convert OAuth2 Client Registration Params to JSON!", e);
throw new IncorrectParameterException("Unable to convert OAuth2 Client Registration Params to JSON!");
}
List<AttributeKvEntry> attributes = new ArrayList<>();
long ts = System.currentTimeMillis();
attributes.add(new BaseAttributeKvEntry(new StringDataEntry(OAUTH2_CLIENT_REGISTRATIONS_PARAMS, json), ts));
try {
// TODO ask if I need here .get()
attributesService.save(tenantId, tenantId, DataConstants.SERVER_SCOPE, attributes).get();
} catch (Exception e) {
log.error("Unable to save OAuth2 Client Registration Params to attributes!", e);
throw new IncorrectParameterException("Unable to save OAuth2 Client Registration Params to attributes!");
}
return getTenantOAuth2ClientsParams(tenantId);
}
@Override
public OAuth2ClientsParams getSystemOAuth2ClientsParams(TenantId tenantId) {
AdminSettings oauth2ClientsParamsSettings = adminSettingsService.findAdminSettingsByKey(tenantId, OAUTH2_CLIENT_REGISTRATIONS_PARAMS);
String json = null;
if (oauth2ClientsParamsSettings != null) {
json = oauth2ClientsParamsSettings.getJsonValue().get(SYSTEM_SETTINGS_OAUTH2_VALUE).asText();
}
return constructOAuth2ClientsParams(json);
}
@Override
public OAuth2ClientsParams getTenantOAuth2ClientsParams(TenantId tenantId) {
ListenableFuture<String> jsonFuture;
if (isOAuth2ClientRegistrationAllowed(tenantId)) {
jsonFuture = getOAuth2ClientsParamsAttribute(tenantId);
} else {
jsonFuture = Futures.immediateFuture("");
}
try {
return Futures.transform(jsonFuture, this::constructOAuth2ClientsParams, MoreExecutors.directExecutor()).get();
} catch (InterruptedException | ExecutionException e) {
log.error("Failed to read OAuth2 Clients Params from attributes!", e);
throw new RuntimeException("Failed to read OAuth2 Clients Params from attributes!", e);
}
}
@Override
public void deleteDomainOAuth2ClientRegistrationByTenant(TenantId tenantId) {
OAuth2ClientsParams params = getTenantOAuth2ClientsParams(tenantId);
if (!StringUtils.isEmpty(params.getDomainName())) {
// TODO don't we need to delete from attributes?
String oauth2ClientsParamsKey = constructClientRegistrationsKey(params.getDomainName());
adminSettingsService.deleteAdminSettingsByKey(tenantId, oauth2ClientsParamsKey);
}
}
@Override
public boolean isOAuth2ClientRegistrationAllowed(TenantId tenantId) {
return false;
Tenant tenant = tenantService.findTenantById(tenantId);
JsonNode allowOAuth2ConfigurationJsonNode = tenant.getAdditionalInfo() != null ? tenant.getAdditionalInfo().get(ALLOW_OAUTH2_CONFIGURATION) : null;
if (allowOAuth2ConfigurationJsonNode == null) {
return true;
} else {
return allowOAuth2ConfigurationJsonNode.asBoolean();
}
}
@Override
@ -110,6 +214,35 @@ public class OAuth2ServiceImpl implements OAuth2Service {
return null;
}
private ListenableFuture<String> getOAuth2ClientsParamsAttribute(TenantId tenantId) {
ListenableFuture<List<AttributeKvEntry>> attributeKvEntriesFuture;
try {
attributeKvEntriesFuture = attributesService.find(tenantId, tenantId, DataConstants.SERVER_SCOPE,
Collections.singletonList(OAUTH2_CLIENT_REGISTRATIONS_PARAMS));
} catch (Exception e) {
log.error("Unable to read OAuth2 Clients Params from attributes!", e);
throw new IncorrectParameterException("Unable to read OAuth2 Clients Params from attributes!");
}
return Futures.transform(attributeKvEntriesFuture, attributeKvEntries -> {
if (attributeKvEntries != null && !attributeKvEntries.isEmpty()) {
AttributeKvEntry kvEntry = attributeKvEntries.get(0);
return kvEntry.getValueAsString();
} else {
return "";
}
}, MoreExecutors.directExecutor());
}
private AdminSettings saveOAuth2ClientSettings(TenantId tenantId, String clientRegistrationsKey) {
AdminSettings oauth2ClientsSettings = new AdminSettings();
oauth2ClientsSettings.setKey(clientRegistrationsKey);
ObjectNode node = mapper.createObjectNode();
node.put("entityType", EntityType.TENANT.name());
node.put("entityId", tenantId.toString());
oauth2ClientsSettings.setJsonValue(node);
return adminSettingsService.saveAdminSettings(tenantId, oauth2ClientsSettings);
}
private String constructClientRegistrationsKey(String domainName) {
String clientRegistrationsKey;
if (StringUtils.isEmpty(domainName)) {
@ -119,4 +252,103 @@ public class OAuth2ServiceImpl implements OAuth2Service {
}
return clientRegistrationsKey;
}
private OAuth2ClientsParams constructOAuth2ClientsParams(String json) {
OAuth2ClientsParams result = null;
if (!StringUtils.isEmpty(json)) {
try {
result = mapper.readValue(json, OAuth2ClientsParams.class);
} catch (IOException e) {
log.error("Unable to read OAuth2 Clients Params from JSON!", e);
throw new IncorrectParameterException("Unable to read OAuth2 Clients Params from JSON!");
}
}
if (result == null) {
result = new OAuth2ClientsParams();
}
return result;
}
private final Consumer<OAuth2ClientRegistration> validator = clientRegistration -> {
if (StringUtils.isEmpty(clientRegistration.getRegistrationId())) {
throw new DataValidationException("Registration ID should be specified!");
}
if (StringUtils.isEmpty(clientRegistration.getClientId())) {
throw new DataValidationException("Client ID should be specified!");
}
if (StringUtils.isEmpty(clientRegistration.getClientSecret())) {
throw new DataValidationException("Client secret should be specified!");
}
if (StringUtils.isEmpty(clientRegistration.getAuthorizationUri())) {
throw new DataValidationException("Authorization uri should be specified!");
}
if (StringUtils.isEmpty(clientRegistration.getTokenUri())) {
throw new DataValidationException("Token uri should be specified!");
}
if (StringUtils.isEmpty(clientRegistration.getRedirectUriTemplate())) {
throw new DataValidationException("Redirect uri template should be specified!");
}
if (StringUtils.isEmpty(clientRegistration.getScope())) {
throw new DataValidationException("Scope should be specified!");
}
if (StringUtils.isEmpty(clientRegistration.getAuthorizationGrantType())) {
throw new DataValidationException("Authorization grant type should be specified!");
}
if (StringUtils.isEmpty(clientRegistration.getUserInfoUri())) {
throw new DataValidationException("User info uri should be specified!");
}
if (StringUtils.isEmpty(clientRegistration.getUserNameAttributeName())) {
throw new DataValidationException("User name attribute name should be specified!");
}
if (StringUtils.isEmpty(clientRegistration.getJwkSetUri())) {
throw new DataValidationException("Jwk set uri should be specified!");
}
if (StringUtils.isEmpty(clientRegistration.getClientAuthenticationMethod())) {
throw new DataValidationException("Client authentication method should be specified!");
}
if (StringUtils.isEmpty(clientRegistration.getClientName())) {
throw new DataValidationException("Client name should be specified!");
}
if (StringUtils.isEmpty(clientRegistration.getLoginButtonLabel())) {
throw new DataValidationException("Login button label should be specified!");
}
OAuth2MapperConfig mapperConfig = clientRegistration.getMapperConfig();
if (mapperConfig == null) {
throw new DataValidationException("Mapper config should be specified!");
}
if (mapperConfig.getType() == null) {
throw new DataValidationException("Mapper config type should be specified!");
}
if (mapperConfig.getType() == MapperType.BASIC) {
OAuth2BasicMapperConfig basicConfig = mapperConfig.getBasicConfig();
if (basicConfig == null) {
throw new DataValidationException("Basic config should be specified!");
}
if (StringUtils.isEmpty(basicConfig.getEmailAttributeKey())) {
throw new DataValidationException("Email attribute key should be specified!");
}
if (basicConfig.getTenantNameStrategy() == null) {
throw new DataValidationException("Tenant name strategy should be specified!");
}
if (basicConfig.getTenantNameStrategy() == TenantNameStrategyType.CUSTOM
&& StringUtils.isEmpty(basicConfig.getTenantNamePattern())) {
throw new DataValidationException("Tenant name pattern should be specified!");
}
}
if (mapperConfig.getType() == MapperType.CUSTOM) {
OAuth2CustomMapperConfig customConfig = mapperConfig.getCustomConfig();
if (customConfig == null) {
throw new DataValidationException("Custom config should be specified!");
}
if (StringUtils.isEmpty(customConfig.getUrl())) {
throw new DataValidationException("Custom mapper URL should be specified!");
}
if (StringUtils.isEmpty(customConfig.getUsername())) {
throw new DataValidationException("Custom mapper username should be specified!");
}
if (StringUtils.isEmpty(customConfig.getPassword())) {
throw new DataValidationException("Custom mapper password should be specified!");
}
}
};
}