temporary commit

This commit is contained in:
Y.Tory 2024-03-18 04:10:27 +00:00
parent 0e1a00f6c3
commit b1734ab893
13 changed files with 159 additions and 49 deletions

View file

@ -67,7 +67,7 @@ public class JAXBWriter {
private final String SERVER_TYPE = "Airsonic-Advanced";
public JAXBWriter() {
Map<String, Object> properties = Map.of(JAXBContext.JAXB_CONTEXT_FACTORY, "org.eclipse.persistence.jaxb.JAXBContextFactory");
Map<String, Object> properties = Map.of(JAXBContext.JAXB_CONTEXT_FACTORY, "org.glassfish.jaxb.runtime.v2.ContextFactory");
Class<?>[] classes = {Response.class};
try {
jaxbContext = JAXBContext.newInstance(classes, properties);

View file

@ -40,7 +40,6 @@ import org.springframework.security.web.context.request.async.WebAsyncManagerInt
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
import java.util.Collections;
import java.util.HashMap;
@ -301,10 +300,12 @@ public class GlobalSecurityConfig {
return http.build();
}
/*
@Bean(name = "mvcHandlerMappingIntrospector")
public HandlerMappingIntrospector mvcHandlerMappingIntrospector() {
return new HandlerMappingIntrospector();
}
*/
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();

View file

@ -31,9 +31,9 @@ import org.airsonic.player.domain.SonosLink;
import org.airsonic.player.repository.SonosLinkRepository;
import org.airsonic.player.service.search.IndexType;
import org.airsonic.player.service.sonos.SonosHelper;
import org.airsonic.player.service.sonos.SonosLinkSecurityInterceptor;
import org.airsonic.player.service.sonos.SonosServiceRegistration;
import org.airsonic.player.service.sonos.SonosSoapFault;
import org.airsonic.player.service.sonos.SonosUtilComponent;
import org.apache.commons.lang.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
@ -117,6 +117,8 @@ public class SonosService implements SonosSoap {
private SonosServiceRegistration registration;
@Autowired
private SonosLinkRepository sonosLinkRepository;
@Autowired
private SonosUtilComponent sonosUtil;
/**
* The context for the request. This is used to get the Auth information
@ -672,7 +674,7 @@ public class SonosService implements SonosSoap {
@Override
public DeviceAuthTokenResult refreshAuthToken() throws CustomFault {
try {
Credentials expiredCreds = SonosLinkSecurityInterceptor.getCredentials(getMessage());
Credentials expiredCreds = sonosUtil.getCredentials(getMessage());
return refreshAuthToken(expiredCreds, getRequest());
} catch (Exception e) {
throw new SonosSoapFault.LoginInvalid();

View file

@ -0,0 +1,44 @@
/*
* This file is part of Airsonic.
*
* Airsonic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Airsonic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Airsonic. If not, see <http://www.gnu.org/licenses/>.
*
* Copyright 2024 (C) Y.Tory
*/
package org.airsonic.player.service.sonos;
import org.eclipse.persistence.jaxb.JAXBContextFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import jakarta.xml.bind.JAXBContext;
import jakarta.xml.bind.JAXBException;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class MoxyConfiguration {
@Bean
public JAXBContext jaxbContext() {
try {
Map<String, Object> properties = new HashMap<>();
return JAXBContextFactory.createContext("com.sonos.services._1", this.getClass().getClassLoader(), properties);
} catch (JAXBException ex) {
throw new RuntimeException(ex);
}
}
}

View file

@ -14,6 +14,7 @@
* You should have received a copy of the GNU General Public License
* along with Airsonic. If not, see <http://www.gnu.org/licenses/>.
*
* Copyright 2024 (C) Y.Tory
* Copyright 2015 (C) Sindre Mehus
*/
@ -47,7 +48,7 @@ public class SonosFaultInterceptor extends AbstractSoapInterceptor {
private static final Logger LOG = LoggerFactory.getLogger(SonosFaultInterceptor.class);
private static Marshaller marshaller = createMarshaller();
private Marshaller marshaller = createMarshaller();
private static Marshaller createMarshaller() {
try {

View file

@ -580,7 +580,6 @@ public class SonosHelper {
result.setItemType(ItemType.TRACK);
result.setMimeType(StringUtil.getMimeType(suffix, true));
result.setTitle(song.getTitle());
result.setGenre(song.getGenre());
result.setIsFavorite(song.getStarredDate() != null);
// result.setDynamic();// TODO: For starred songs
@ -594,6 +593,7 @@ public class SonosHelper {
trackMetadata.setAlbumArtURI(albumArtURI);
trackMetadata.setDuration((int) Math.round(song.getDuration()));
trackMetadata.setTrackNumber(song.getTrackNumber());
trackMetadata.setGenre(song.getGenre());
MediaFile parent = mediaFileService.getParentOf(song);
if (parent != null && parent.isAlbum()) {

View file

@ -2,7 +2,6 @@ package org.airsonic.player.service.sonos;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.google.common.collect.Sets;
import com.sonos.services._1.Credentials;
import com.sonos.services._1.DeviceAuthTokenResult;
import com.sonos.services._1.RefreshAuthTokenResponse;
import org.airsonic.player.domain.SonosLink;
@ -16,7 +15,6 @@ import org.airsonic.player.service.SonosService;
import org.apache.commons.codec.binary.StringUtils;
import org.apache.cxf.binding.soap.SoapMessage;
import org.apache.cxf.binding.soap.interceptor.AbstractSoapInterceptor;
import org.apache.cxf.headers.Header;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.phase.Phase;
import org.apache.cxf.transport.http.AbstractHTTPDestination;
@ -28,13 +26,8 @@ import org.springframework.security.authentication.CredentialsExpiredException;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.w3c.dom.Node;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.xml.bind.JAXBContext;
import jakarta.xml.bind.JAXBException;
import jakarta.xml.bind.Unmarshaller;
import javax.xml.namespace.QName;
import java.util.Set;
@ -75,15 +68,8 @@ public class SonosLinkSecurityInterceptor extends AbstractSoapInterceptor {
@Autowired
private SonosService sonosService;
private static Unmarshaller unmarshaller = createUnmarshaller();
private static Unmarshaller createUnmarshaller() {
try {
return JAXBContext.newInstance("com.sonos.services._1").createUnmarshaller();
} catch (JAXBException e) {
throw new AssertionError(e);
}
}
@Autowired
private SonosUtilComponent sonosUtil;
@Override
public void handleMessage(SoapMessage message) throws Fault {
@ -102,7 +88,7 @@ public class SonosLinkSecurityInterceptor extends AbstractSoapInterceptor {
LOG.debug("No auth required for SOAP message: {}", message.toString());
} else if (action != null && authenticationType == AuthenticationType.APPLICATION_LINK) {
String sonosLinkToken = getCredentials(message).getLoginToken().getToken();
String sonosLinkToken = sonosUtil.getCredentials(message).getLoginToken().getToken();
if (sonosLinkToken != null) {
JWTAuthenticationToken token = new JWTAuthenticationToken(null, sonosLinkToken,
request.getRequestURI().substring(request.getContextPath().length() + 1) + "?" + request.getQueryString());
@ -121,7 +107,7 @@ public class SonosLinkSecurityInterceptor extends AbstractSoapInterceptor {
return;
}
try {
DeviceAuthTokenResult token = sonosService.refreshAuthToken(getCredentials(message), request);
DeviceAuthTokenResult token = sonosService.refreshAuthToken(sonosUtil.getCredentials(message), request);
RefreshAuthTokenResponse resp = new RefreshAuthTokenResponse();
resp.setRefreshAuthTokenResult(token);
throw new SonosSoapFault.TokenRefreshRequired(resp);
@ -138,17 +124,6 @@ public class SonosLinkSecurityInterceptor extends AbstractSoapInterceptor {
}
}
public static Credentials getCredentials(SoapMessage message) throws JAXBException {
QName credentialQName = new QName("http://www.sonos.com/Services/1.1", "credentials");
for (Header header : message.getHeaders()) {
if (credentialQName.equals(header.getName())) {
return unmarshaller.unmarshal((Node) header.getObject(), Credentials.class).getValue();
}
}
return null;
}
private String getAction(SoapMessage message) {
Object soapAction = message.get("SOAPAction");

View file

@ -0,0 +1,45 @@
package org.airsonic.player.service.sonos;
import com.sonos.services._1.Credentials;
import org.apache.cxf.binding.soap.SoapMessage;
import org.apache.cxf.headers.Header;
import org.springframework.stereotype.Component;
import org.w3c.dom.Node;
import jakarta.xml.bind.JAXBContext;
import jakarta.xml.bind.JAXBException;
import jakarta.xml.bind.Unmarshaller;
import javax.xml.namespace.QName;
import java.util.Map;
@Component
public class SonosUtilComponent {
private Unmarshaller unmarshaller = createUnmarshaller();
private Unmarshaller createUnmarshaller() {
Class<?>[] classes = {Credentials.class};
Map<String, Object> properties = Map.of(JAXBContext.JAXB_CONTEXT_FACTORY, "org.eclipse.persistence.jaxb.JAXBContextFactory");
try {
return JAXBContext.newInstance(classes, properties).createUnmarshaller();
} catch (JAXBException e) {
throw new AssertionError(e);
}
}
public Credentials getCredentials(SoapMessage message) throws JAXBException {
QName credentialQName = new QName("http://www.sonos.com/Services/0.1", "credentials");
for (Header header : message.getHeaders()) {
if (credentialQName.equals(header.getName())) {
return unmarshaller.unmarshal((Node) header.getObject(), Credentials.class).getValue();
}
}
return null;
}
}

View file

@ -4,25 +4,33 @@ import org.airsonic.player.service.SonosService;
import org.airsonic.player.service.sonos.SonosFaultInterceptor;
import org.airsonic.player.service.sonos.SonosLinkSecurityInterceptor;
import org.apache.cxf.Bus;
import org.apache.cxf.jaxb.JAXBDataBinding;
import org.apache.cxf.jaxws.EndpointImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;
import jakarta.xml.bind.JAXBContext;
import jakarta.xml.ws.Endpoint;
import java.util.Collections;
@Configuration
@ImportResource({"classpath:META-INF/cxf/cxf.xml", "classpath:META-INF/cxf/cxf-servlet.xml"})
public class SonosConfiguration {
@Autowired
private JAXBContext jaxbContext;
@Bean
public Endpoint sonosEndpoint(Bus bus, SonosService sonosService,
SonosFaultInterceptor sonosFaultInterceptor,
SonosLinkSecurityInterceptor sonosSecurity) {
EndpointImpl endpoint = new EndpointImpl(bus, sonosService);
endpoint.setOutFaultInterceptors(Collections.singletonList(sonosFaultInterceptor));
endpoint.setDataBinding(new JAXBDataBinding(jaxbContext));
endpoint.setInInterceptors(Collections.singletonList(sonosSecurity));
endpoint.publish("/Sonos");
return endpoint;

View file

@ -31,7 +31,7 @@
<plugin>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-codegen-plugin</artifactId>
<version>${cxf.version}</version>
<version>${cxf.version}</version><!-- 4.x is not compatible with eclispse MoXY-->
<executions>
<execution>
<id>generate-sources</id>
@ -50,6 +50,29 @@
</execution>
</executions>
</plugin>
<!--<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<phase>generate-sources</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<target>
<fileset dir="${project.build.directory}/generated-sources/cxf" includes="**/*.java">
<filename name="**/*.java"/>
</fileset>
<replace dir="${project.build.directory}/generated-sources/cxf" includes="**/*.java" token="javax.xml.ws" value="jakarta.xml.ws"/>
<replace dir="${project.build.directory}/generated-sources/cxf" includes="**/*.java" token="javax.xml.bind" value="jakarta.xml.bind"/>
<replace dir="${project.build.directory}/generated-sources/cxf" includes="**/*.java" token="javax.jws" value="jakarta.jws"/>
</target>
</configuration>
</execution>
</executions>
</plugin>-->
</plugins>
</build>
</project>

View file

@ -25,7 +25,7 @@
<failOnDependencyWarning>true</failOnDependencyWarning>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>17</java.version>
<cxf.version>4.0.3</cxf.version>
<cxf.version>4.0.4</cxf.version>
<snakeyaml.version>2.2</snakeyaml.version>
<docker.container.repo>ghcr.io/kagemomiji/airsonic-advanced</docker.container.repo>
</properties>
@ -329,6 +329,7 @@
<ignoredUsedUndeclaredDependency>org.springframework.data:spring-data-commons:*</ignoredUsedUndeclaredDependency>
<ignoredUsedUndeclaredDependency>commons-collections:commons-collections:*</ignoredUsedUndeclaredDependency>
<ignoredUsedUndeclaredDependency>com.sun.mail:jakarta.mail*</ignoredUsedUndeclaredDependency>
<ignoredUsedUndeclaredDependency>org.glassfish.jaxb:jaxb-runtime</ignoredUsedUndeclaredDependency>
</ignoredUsedUndeclaredDependencies>
<ignoredUnusedDeclaredDependencies>
<ignoredUnusedDeclaredDependency>com.google.code.findbugs:jsr305:*</ignoredUnusedDeclaredDependency>
@ -349,7 +350,9 @@
<ignoredUnusedDeclaredDependency>jakarta.annotation:jakarta.annotation-api</ignoredUnusedDeclaredDependency>
<ignoredUnusedDeclaredDependency>org.springframework:spring-aspects</ignoredUnusedDeclaredDependency>
<ignoredUnusedDeclaredDependency>org.yaml:snakeyaml</ignoredUnusedDeclaredDependency>
<ignoredUnusedDeclaredDependency>org.glassfish.jaxb:jaxb-runtime</ignoredUnusedDeclaredDependency>
<ignoredUnusedDeclaredDependency>org.eclipse.persistence:org.eclipse.persistence.moxy</ignoredUnusedDeclaredDependency>
<ignoredUnusedDeclaredDependency>org.apache.cxf:cxf-tools-wsdlto-databinding-jaxb</ignoredUnusedDeclaredDependency>
<ignoredUsedUndeclaredDependency>org.glassfish.jaxb:jaxb-runtime</ignoredUsedUndeclaredDependency>
</ignoredUnusedDeclaredDependencies>
</configuration>
</execution>

View file

@ -28,23 +28,29 @@
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>jaxb2-maven-plugin</artifactId>
<version>3.1.0</version>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-xjc-plugin</artifactId>
<version>4.0.0</version>
<configuration>
<sourceRoot>${basedir}/target/generated/src/main/java</sourceRoot>
<xsdOptions>
<xsdOption>
<xsd>${basedir}/src/main/resources/subsonic-rest-api.xsd</xsd>
<packagename>org.subsonic.restapi</packagename>
</xsdOption>
</xsdOptions>
</configuration>
<executions>
<execution>
<id>xjc</id>
<goals>
<goal>xjc</goal>
</goals>
<goals>
<goal>xsdtojava</goal>
</goals>
<phase>generate-sources</phase>
</execution>
</executions>
<configuration>
<sources>
<source>src/main/resources/subsonic-rest-api.xsd</source>
</sources>
</configuration>
</plugin>
</plugins>
</build>
</project>

View file

@ -1,5 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:jaxb="https://jakarta.ee/xml/ns/jaxb"
jaxb:version="3.0"
xmlns:sub="http://subsonic.org/restapi"
targetNamespace="http://subsonic.org/restapi"
attributeFormDefault="unqualified"