| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License") + you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.openmeetings.db.dao.basic; |
| |
| import static org.apache.commons.lang3.math.NumberUtils.toInt; |
| import static org.apache.openmeetings.db.util.DaoHelper.setLimits; |
| import static org.apache.openmeetings.util.OpenmeetingsVariables.*; |
| import static org.apache.wicket.csp.CSPDirectiveSrcValue.SELF; |
| import static org.apache.wicket.csp.CSPDirectiveSrcValue.STRICT_DYNAMIC; |
| |
| import java.net.UnknownHostException; |
| import java.util.ArrayList; |
| import java.util.Date; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Set; |
| import java.util.TimeZone; |
| |
| import javax.persistence.EntityManager; |
| import javax.persistence.PersistenceContext; |
| import javax.persistence.TypedQuery; |
| |
| import org.apache.openjpa.conf.OpenJPAConfiguration; |
| import org.apache.openjpa.event.RemoteCommitProvider; |
| import org.apache.openjpa.event.TCPRemoteCommitProvider; |
| import org.apache.openjpa.persistence.OpenJPAEntityManagerSPI; |
| import org.apache.openjpa.persistence.OpenJPAPersistence; |
| import org.apache.openmeetings.IApplication; |
| import org.apache.openmeetings.db.dao.IDataProviderDao; |
| import org.apache.openmeetings.db.dao.server.OAuth2Dao; |
| import org.apache.openmeetings.db.dao.user.UserDao; |
| import org.apache.openmeetings.db.entity.basic.Configuration; |
| import org.apache.openmeetings.db.util.DaoHelper; |
| import org.apache.openmeetings.util.crypt.CryptProvider; |
| import org.apache.wicket.Application; |
| import org.apache.wicket.csp.CSPDirective; |
| import org.apache.wicket.csp.CSPHeaderConfiguration; |
| import org.apache.wicket.protocol.http.WebApplication; |
| import org.apache.wicket.util.string.Strings; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| import org.springframework.beans.factory.annotation.Autowired; |
| import org.springframework.stereotype.Repository; |
| import org.springframework.transaction.annotation.Transactional; |
| |
| import com.github.openjson.JSONObject; |
| |
| /** |
| * Insert/update/Delete on {@link Configuration} |
| * |
| * It provides basic mechanism to get a Conf Key: |
| * {@link #getBool(String, boolean)} |
| * {@link #getLong(String, Long)} |
| * {@link #getInt(String, int)} |
| * {@link #getString(String, String)} |
| * |
| * <b> {@link #get(String)} is deprecated!</b> |
| * |
| * @author swagner |
| * |
| */ |
| @Repository |
| @Transactional |
| public class ConfigurationDao implements IDataProviderDao<Configuration> { |
| private static final Logger log = LoggerFactory.getLogger(ConfigurationDao.class); |
| private static final String[] searchFields = {"key", "value"}; |
| |
| @PersistenceContext |
| private EntityManager em; |
| |
| @Autowired |
| private UserDao userDao; |
| @Autowired |
| private OAuth2Dao oauthDao; |
| @Autowired |
| private IApplication app; |
| |
| public void updateClusterAddresses(String addresses) throws UnknownHostException { |
| OpenJPAConfiguration cfg = ((OpenJPAEntityManagerSPI)OpenJPAPersistence.cast(em)).getConfiguration(); |
| RemoteCommitProvider prov = cfg.getRemoteCommitEventManager().getRemoteCommitProvider(); |
| if (prov instanceof TCPRemoteCommitProvider) { |
| ((TCPRemoteCommitProvider)prov).setAddresses(addresses); |
| } |
| } |
| |
| /** |
| * Retrieves Configuration regardless of its deleted status |
| * |
| * @param key - key of the {@link Configuration} to get |
| * @return correspondent {@link Configuration} or null |
| */ |
| public Configuration forceGet(String key) { |
| try { |
| List<Configuration> list = em.createNamedQuery("forceGetConfigurationByKey", Configuration.class) |
| .setParameter("key", key).getResultList(); |
| if (list.isEmpty()) { |
| return null; |
| } |
| Configuration c = list.get(0); |
| return c.getKey().equals(key) ? c : null; |
| } catch (Exception e) { |
| log.error("[forceGet]: ", e); |
| } |
| return null; |
| } |
| |
| public List<Configuration> get(String... keys) { |
| List<Configuration> result = new ArrayList<>(); |
| for (String key : keys) { //iteration is necessary to fill list with all values |
| List<Configuration> r = em.createNamedQuery("getConfigurationsByKeys", Configuration.class) |
| .setParameter("keys", List.of(key)) |
| .getResultList(); |
| result.add(r.isEmpty() ? null : r.get(0)); |
| } |
| return result; |
| } |
| |
| public Configuration get(String key) { |
| List<Configuration> list = get(new String[] {key}); |
| |
| if (list == null || list.isEmpty() || list.get(0) == null) { |
| log.warn("Could not find key in configurations: {}", key); |
| return null; |
| } |
| return list.get(0); |
| } |
| |
| public boolean getBool(String key, boolean def) { |
| Configuration c = get(key); |
| |
| if (c != null) { |
| try { |
| return c.getValueB(); |
| } catch (Exception e) { |
| //no-op, parsing exception |
| } |
| } |
| return def; |
| } |
| |
| public Long getLong(String key, Long def) { |
| Configuration c = get(key); |
| |
| if (c != null) { |
| try { |
| return c.getValueN(); |
| } catch (Exception e) { |
| //no-op, parsing exception |
| } |
| } |
| return def; |
| } |
| |
| public int getInt(String key, int def) { |
| Configuration c = get(key); |
| |
| if (c != null) { |
| try { |
| Long val = c.getValueN(); |
| return val == null ? def : val.intValue(); |
| } catch (Exception e) { |
| //no-op, parsing exception |
| } |
| } |
| return def; |
| } |
| |
| public String getString(String key, String def) { |
| Configuration c = get(key); |
| return c != null && c.getValue() != null ? c.getValue() : def; |
| } |
| |
| @Override |
| public Configuration get(Long id) { |
| if (id == null) { |
| return null; |
| } |
| return em.createNamedQuery("getConfigurationById", Configuration.class) |
| .setParameter("id", id).getSingleResult(); |
| } |
| |
| @Override |
| public List<Configuration> get(long start, long count) { |
| return setLimits(em.createNamedQuery("getNondeletedConfiguration", Configuration.class) |
| , start, count).getResultList(); |
| } |
| |
| @Override |
| public List<Configuration> get(String search, long start, long count, String sort) { |
| return setLimits(em.createQuery(DaoHelper.getSearchQuery("Configuration", "c", search, true, false, sort, searchFields), Configuration.class) |
| , start, count).getResultList(); |
| } |
| |
| @Override |
| public long count() { |
| return em.createNamedQuery("countConfigurations", Long.class).getSingleResult(); |
| } |
| |
| @Override |
| public long count(String search) { |
| TypedQuery<Long> q = em.createQuery(DaoHelper.getSearchQuery("Configuration", "c", search, true, true, null, searchFields), Long.class); |
| return q.getSingleResult(); |
| } |
| |
| @Override |
| public Configuration update(Configuration entity, Long userId) { |
| return update(entity, userId, false); |
| } |
| |
| public Configuration update(Configuration entity, Long userId, boolean deleted) { |
| String key = entity.getKey(); |
| String value = entity.getValue(); |
| if (entity.getId() == null || entity.getId().longValue() <= 0) { |
| entity.setInserted(new Date()); |
| entity.setDeleted(deleted); |
| em.persist(entity); |
| } else { |
| entity.setUser(userDao.get(userId)); |
| entity.setDeleted(deleted); |
| entity.setUpdated(new Date()); |
| entity = em.merge(entity); |
| } |
| switch (key) { |
| case CONFIG_CAM_FPS: |
| case CONFIG_MIC_ECHO: |
| case CONFIG_MIC_NOISE: |
| case CONFIG_MIC_RATE: |
| case CONFIG_KEYCODE_ARRANGE: |
| case CONFIG_KEYCODE_MUTE_OTHERS: |
| case CONFIG_KEYCODE_MUTE: |
| case CONFIG_KEYCODE_QUICKPOLL: |
| case CONFIG_KEYCODE_ARRANGE_RESIZE: |
| case CONFIG_AUTO_OPEN_SHARING: |
| reloadRoomSettings(); |
| break; |
| case CONFIG_MAX_UPLOAD_SIZE: |
| reloadMaxUpload(); |
| break; |
| case CONFIG_CRYPT: |
| reloadCrypt(); |
| break; |
| case CONFIG_APPLICATION_NAME: |
| setApplicationName(value); |
| break; |
| case CONFIG_APPLICATION_BASE_URL: |
| reloadBaseUrl(); |
| break; |
| case CONFIG_SIP_ENABLED: |
| reloadSipEnabled(); |
| break; |
| case CONFIG_EXT_PROCESS_TTL: |
| setExtProcessTtl(toInt(value)); |
| break; |
| case CONFIG_DEFAULT_LANG: |
| reloadDefaultLang(); |
| break; |
| case CONFIG_MP4_AUDIO_RATE: |
| reloadAudioRate(); |
| break; |
| case CONFIG_MP4_AUDIO_BITRATE: |
| reloadAudioBitrate(); |
| break; |
| case CONFIG_MP4_VIDEO_PRESET: |
| reloadVideoPreset(); |
| break; |
| case CONFIG_DEFAULT_TIMEZONE: |
| reloadTimezone(); |
| break; |
| case CONFIG_REST_ALLOW_ORIGIN: |
| reloadRestAllowOrigin(); |
| break; |
| case CONFIG_LOGIN_MIN_LENGTH: |
| reloadLoginMinLength(); |
| break; |
| case CONFIG_PASS_MIN_LENGTH: |
| reloadPasswdMinLength(); |
| break; |
| case CONFIG_DEFAULT_GROUP_ID: |
| reloadDefaultGroup(); |
| break; |
| case CONFIG_SIP_EXTEN_CONTEXT: |
| reloadSipContext(); |
| break; |
| case CONFIG_FNAME_MIN_LENGTH: |
| reloadFnameMinLength(); |
| break; |
| case CONFIG_LNAME_MIN_LENGTH: |
| reloadLnameMinLength(); |
| break; |
| case CONFIG_CHAT_SEND_ON_ENTER: |
| reloadChatSendOnEnter(); |
| break; |
| case CONFIG_REGISTER_FRONTEND: |
| reloadAllowRegisterFront(); |
| break; |
| case CONFIG_REGISTER_SOAP: |
| reloadAllowRegisterSoap(); |
| break; |
| case CONFIG_REGISTER_OAUTH: |
| reloadAllowRegisterOauth(); |
| break; |
| case CONFIG_EMAIL_VERIFICATION: |
| reloadSendVerificationEmail(); |
| break; |
| case CONFIG_EMAIL_AT_REGISTER: |
| reloadSendRegisterEmail(); |
| break; |
| case CONFIG_DISPLAY_NAME_EDITABLE: |
| reloadDisplayNameEditable(); |
| break; |
| case CONFIG_MYROOMS_ENABLED: |
| reloadMyRoomsEnabled(); |
| break; |
| case CONFIG_GOOGLE_ANALYTICS_CODE: |
| case CONFIG_CSP_FONT: |
| case CONFIG_CSP_FRAME: |
| case CONFIG_CSP_IMAGE: |
| case CONFIG_CSP_MEDIA: |
| case CONFIG_CSP_SCRIPT: |
| case CONFIG_CSP_STYLE: |
| updateCsp(); |
| break; |
| case CONFIG_SMTP_SERVER: |
| case CONFIG_SMTP_PORT: |
| case CONFIG_SMTP_SYSTEM_EMAIL: |
| case CONFIG_SMTP_USER: |
| case CONFIG_SMTP_PASS: |
| case CONFIG_SMTP_TLS: |
| case CONFIG_SMTP_SSL: |
| case CONFIG_REPLY_TO_ORGANIZER: |
| case CONFIG_SMTP_TIMEOUT_CON: |
| case CONFIG_SMTP_TIMEOUT: |
| reloadMailSettings(); |
| break; |
| } |
| return entity; |
| } |
| |
| @Override |
| public void delete(Configuration entity, Long userId) { |
| entity.setUpdated(new Date()); |
| this.update(entity, userId, true); |
| } |
| |
| private void reloadMaxUpload() { |
| try { |
| setMaxUploadSize(getLong(CONFIG_MAX_UPLOAD_SIZE, DEFAULT_MAX_UPLOAD_SIZE)); |
| } catch (Exception e) { |
| log.error("Invalid value saved for max_upload_size conf key: ", e); |
| } |
| } |
| |
| private void reloadCrypt() { |
| String cryptClass = getString(CONFIG_CRYPT, null); |
| if (cryptClass != null) { |
| setCryptClassName(cryptClass); |
| CryptProvider.reset(); |
| } |
| } |
| |
| private void reloadBaseUrl() { |
| String val = getString(CONFIG_APPLICATION_BASE_URL, DEFAULT_BASE_URL); |
| if (val != null && !val.endsWith("/")) { |
| val += "/"; |
| } |
| setBaseUrl(val); |
| } |
| |
| private void reloadSipEnabled() { |
| setSipEnabled(getBool(CONFIG_SIP_ENABLED, false)); |
| } |
| |
| private void reloadDefaultLang() { |
| setDefaultLang(getLong(CONFIG_DEFAULT_LANG, 1L)); |
| } |
| |
| private void reloadAudioRate() { |
| setAudioRate(getInt(CONFIG_MP4_AUDIO_RATE, 22050)); |
| } |
| |
| private void reloadAudioBitrate() { |
| setAudioBitrate(getString(CONFIG_MP4_AUDIO_BITRATE, "32k")); |
| } |
| |
| private void reloadVideoPreset() { |
| setVideoPreset(getString(CONFIG_MP4_VIDEO_PRESET, "medium")); |
| } |
| |
| private void reloadTimezone() { |
| String defaultTzName = getString(CONFIG_DEFAULT_TIMEZONE, "Europe/Berlin"); |
| |
| TimeZone timeZoneByOmTimeZone = TimeZone.getTimeZone(defaultTzName); |
| |
| if (timeZoneByOmTimeZone == null) { //this seems to be impossible |
| // If everything fails take the servers default one |
| log.error("There is no correct time zone set in the configuration of OpenMeetings for the key default.timezone or key is missing in table, using default locale!"); |
| defaultTzName = TimeZone.getDefault().getID(); |
| } |
| setDefaultTimezone(defaultTzName); |
| } |
| |
| private void reloadRestAllowOrigin() { |
| setRestAllowOrigin(getString(CONFIG_REST_ALLOW_ORIGIN, null)); |
| } |
| |
| private void reloadLoginMinLength() { |
| setMinLoginLength(getInt(CONFIG_LOGIN_MIN_LENGTH, USER_LOGIN_MINIMUM_LENGTH)); |
| } |
| |
| private void reloadPasswdMinLength() { |
| setMinPasswdLength(getInt(CONFIG_LOGIN_MIN_LENGTH, USER_PASSWORD_MINIMUM_LENGTH)); |
| } |
| |
| private void reloadDefaultGroup() { |
| setDefaultGroup(getLong(CONFIG_DEFAULT_GROUP_ID, null)); |
| } |
| |
| private void reloadSipContext() { |
| setSipContext(getString(CONFIG_SIP_EXTEN_CONTEXT, DEFAULT_SIP_CONTEXT)); |
| } |
| |
| private void reloadFnameMinLength() { |
| setMinFnameLength(getInt(CONFIG_FNAME_MIN_LENGTH, USER_LOGIN_MINIMUM_LENGTH)); |
| } |
| |
| private void reloadLnameMinLength() { |
| setMinLnameLength(getInt(CONFIG_LNAME_MIN_LENGTH, USER_LOGIN_MINIMUM_LENGTH)); |
| } |
| |
| private void reloadChatSendOnEnter() { |
| setChatSendOnEnter(getBool(CONFIG_CHAT_SEND_ON_ENTER, false)); |
| } |
| |
| private void reloadAllowRegisterFront() { |
| setAllowRegisterFrontend(getBool(CONFIG_REGISTER_FRONTEND, false)); |
| } |
| |
| private void reloadAllowRegisterSoap() { |
| setAllowRegisterSoap(getBool(CONFIG_REGISTER_SOAP, false)); |
| } |
| |
| private void reloadAllowRegisterOauth() { |
| setAllowRegisterOauth(getBool(CONFIG_REGISTER_OAUTH, false)); |
| } |
| |
| private void reloadSendVerificationEmail() { |
| setSendVerificationEmail(getBool(CONFIG_EMAIL_VERIFICATION, false)); |
| } |
| |
| private void reloadSendRegisterEmail() { |
| setSendRegisterEmail(getBool(CONFIG_EMAIL_AT_REGISTER, false)); |
| } |
| |
| private void reloadDisplayNameEditable() { |
| setDisplayNameEditable(getBool(CONFIG_DISPLAY_NAME_EDITABLE, false)); |
| } |
| |
| private void reloadMyRoomsEnabled() { |
| setMyRoomsEnabled(getBool(CONFIG_MYROOMS_ENABLED, true)); |
| } |
| |
| private void reloadMailSettings() { |
| setSmtpServer(getString(CONFIG_SMTP_SERVER, null)); |
| setSmtpPort(getInt(CONFIG_SMTP_PORT, 25)); |
| setSmtpUser(getString(CONFIG_SMTP_USER, null)); |
| setSmtpPass(getString(CONFIG_SMTP_PASS, null)); |
| setSmtpUseTls(getBool(CONFIG_SMTP_TLS, false)); |
| setSmtpTimeOut(getInt(CONFIG_SMTP_TIMEOUT, 30000)); |
| setSmtpConnectionTimeOut(getInt(CONFIG_SMTP_TIMEOUT_CON, 30000)); |
| setMailFrom(getString(CONFIG_SMTP_SYSTEM_EMAIL, null)); |
| setMailAddReplyTo(getBool(CONFIG_REPLY_TO_ORGANIZER, true)); |
| } |
| |
| public void reinit() { |
| reloadMaxUpload(); |
| reloadCrypt(); |
| setApplicationName(getString(CONFIG_APPLICATION_NAME, DEFAULT_APP_NAME)); |
| reloadDefaultLang(); |
| reloadBaseUrl(); |
| reloadSipEnabled(); |
| reloadAudioRate(); |
| reloadAudioBitrate(); |
| reloadVideoPreset(); |
| reloadTimezone(); |
| reloadRestAllowOrigin(); |
| reloadRoomSettings(); |
| reloadLoginMinLength(); |
| reloadPasswdMinLength(); |
| reloadDefaultGroup(); |
| reloadSipContext(); |
| reloadFnameMinLength(); |
| reloadLnameMinLength(); |
| reloadChatSendOnEnter(); |
| reloadAllowRegisterFront(); |
| reloadAllowRegisterSoap(); |
| reloadAllowRegisterOauth(); |
| reloadSendVerificationEmail(); |
| reloadSendRegisterEmail(); |
| reloadDisplayNameEditable(); |
| reloadMyRoomsEnabled(); |
| reloadMailSettings(); |
| |
| updateCsp(); |
| } |
| |
| private static JSONObject getHotkey(String value) { |
| List<String> partList = List.of(value.split("\\+")); |
| Set<String> parts = new HashSet<>(partList); |
| return new JSONObject() |
| .put("alt", parts.contains("Alt")) |
| .put("shift", parts.contains("Shift")) |
| .put("ctrl", parts.contains("Ctrl")) |
| .put("code", partList.get(partList.size() - 1)); |
| } |
| |
| private JSONObject reloadRoomSettings() { |
| try { |
| setRoomSettings(new JSONObject() |
| .put("keycode", new JSONObject() |
| .put("arrange", getHotkey(getString(CONFIG_KEYCODE_ARRANGE, "Shift+F8"))) |
| .put("arrangeresize", getHotkey(getString(CONFIG_KEYCODE_ARRANGE_RESIZE, "Ctrl+Shift+KeyA"))) |
| .put("muteothers", getHotkey(getString(CONFIG_KEYCODE_MUTE_OTHERS, "Shift+F12"))) |
| .put("mute", getHotkey(getString(CONFIG_KEYCODE_MUTE, "Shift+F7"))) |
| .put("quickpoll", getHotkey(getString(CONFIG_KEYCODE_QUICKPOLL, "Ctrl+Alt+KeyQ"))) |
| ) |
| .put("camera", new JSONObject().put("fps", getLong(CONFIG_CAM_FPS, 30L))) |
| .put("microphone", new JSONObject() |
| .put("rate", getLong(CONFIG_MIC_RATE, 30L)) |
| .put("echo", getBool(CONFIG_MIC_ECHO, true)) |
| .put("noise", getBool(CONFIG_MIC_NOISE, true)) |
| ) |
| .put("autoOpenSharing", getBool(CONFIG_AUTO_OPEN_SHARING, false)) |
| ); |
| } catch (Exception e) { |
| log.error("Unexpected exception while reloading room settings: ", e); |
| } |
| return getRoomSettings(); |
| } |
| |
| private void addCspRule(CSPHeaderConfiguration cspConfig, CSPDirective key, String val) { |
| addCspRule(cspConfig, key, val, true); |
| } |
| |
| private void addCspRule(CSPHeaderConfiguration cspConfig, CSPDirective key, String val, boolean remove) { |
| if (!Strings.isEmpty(val)) { |
| for(String str : val.split(",")) { |
| if (!Strings.isEmpty(str)) { |
| cspConfig.add(key, str.trim()); |
| } |
| } |
| } else if (remove) { |
| cspConfig.remove(key); |
| } |
| } |
| |
| public void updateCsp() { |
| setGaCode(getString(CONFIG_GOOGLE_ANALYTICS_CODE, null)); |
| |
| setCspFontSrc(getString(CONFIG_CSP_FONT, DEFAULT_CSP_FONT)); |
| setCspFrameSrc(getString(CONFIG_CSP_FRAME, SELF.getValue())); |
| setCspImageSrc(getString(CONFIG_CSP_IMAGE, DEFAULT_CSP_IMAGE)); |
| setCspMediaSrc(getString(CONFIG_CSP_MEDIA, SELF.getValue())); |
| setCspScriptSrc(getString(CONFIG_CSP_SCRIPT, STRICT_DYNAMIC.getValue())); |
| setCspStyleSrc(getString(CONFIG_CSP_STYLE, DEFAULT_CSP_STYLE)); |
| if (Application.exists()) { |
| final CSPHeaderConfiguration cspConfig = WebApplication.get().getCspSettings().blocking().strict(); |
| addCspRule(cspConfig, CSPDirective.FONT_SRC, getCspFontSrc()); |
| addCspRule(cspConfig, CSPDirective.FRAME_SRC, getCspFrameSrc()); |
| addCspRule(cspConfig, CSPDirective.IMG_SRC, getCspImageSrc()); |
| addCspRule(cspConfig, CSPDirective.MEDIA_SRC, getCspMediaSrc()); |
| addCspRule(cspConfig, CSPDirective.SCRIPT_SRC, getCspScriptSrc()); |
| addCspRule(cspConfig, CSPDirective.STYLE_SRC, getCspStyleSrc()); |
| addCspRule(cspConfig, CSPDirective.CONNECT_SRC, app.getWsUrl(), false); // special code for Safari browser |
| if (!Strings.isEmpty(getGaCode())) { |
| // https://developers.google.com/tag-manager/web/csp#universal_analytics_google_analytics |
| addCspRule(cspConfig, CSPDirective.IMG_SRC, "https://www.google-analytics.com"); |
| addCspRule(cspConfig, CSPDirective.SCRIPT_SRC, "https://www.google-analytics.com, https://ssl.google-analytics.com"); |
| } |
| oauthDao.getActive().forEach(oauth -> { |
| if (!Strings.isEmpty(oauth.getIconUrl())) { |
| addCspRule(cspConfig, CSPDirective.IMG_SRC, oauth.getIconUrl(), false); |
| } |
| }); |
| } |
| } |
| } |