Skip to content

Commit

Permalink
#474 fix auth bug at stream endpoint from upnp player
Browse files Browse the repository at this point in the history
  • Loading branch information
kagemomiji committed May 29, 2024
1 parent 97a37d9 commit d35a121
Show file tree
Hide file tree
Showing 19 changed files with 656 additions and 538 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -114,12 +114,13 @@ public ResponseEntity<Resource> handleRequest(Authentication authentication,
@RequestParam(required = false, name = "offsetSeconds") Double offsetSeconds,
ServletWebRequest swr) throws Exception {

User user = securityService.getCurrentUser(swr.getRequest());
String username = securityService.getCurrentUsername(swr.getRequest());
User user = securityService.getUserByName(username);
if (!(authentication instanceof JWTAuthenticationToken) && !user.isStreamRole()) {
throw new AccessDeniedException("Streaming is forbidden for user " + user.getUsername());
throw new AccessDeniedException("Streaming is forbidden for user " + username);
}

Player player = playerService.getPlayer(swr.getRequest(), swr.getResponse(), user.getUsername(), false, true);
Player player = playerService.getPlayer(swr.getRequest(), swr.getResponse(), username, false, true);

Long expectedSize = null;

Expand Down Expand Up @@ -154,8 +155,8 @@ public ResponseEntity<Resource> handleRequest(Authentication authentication,
if (isSingleFile) {

if (!(authentication instanceof JWTAuthenticationToken)
&& !securityService.isFolderAccessAllowed(file, user.getUsername())) {
throw new AccessDeniedException("Access to file " + file.getId() + " is forbidden for user " + user.getUsername());
&& !securityService.isFolderAccessAllowed(file, username)) {
throw new AccessDeniedException("Access to file " + file.getId() + " is forbidden for user " + username);
}

// Update the index of the currently playing media file. At
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import jakarta.annotation.Nullable;
import jakarta.servlet.http.HttpServletRequest;

import java.io.IOException;
Expand Down Expand Up @@ -307,7 +308,6 @@ public List<UserCredential> getCredentials(String username, App... apps) {
return userRepository.findByUsername(username).map(user -> {
return userCredentialRepository.findByUserAndAppIn(user, List.of(apps));
}).orElseGet(() -> {
LOG.warn("Can't get credentials for a non-existent user {}", username);
return Collections.emptyList();
});
}
Expand Down Expand Up @@ -420,7 +420,8 @@ public String getCurrentUsername(HttpServletRequest request) {
* @param username The username used when logging in.
* @return The user, or <code>null</code> if not found.
*/
public User getUserByName(String username) {
@Nullable
public User getUserByName(@Nullable String username) {
return getUserByName(username, true);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@
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 2016 (C) Airsonic Authors
Based upon Subsonic, Copyright 2009 (C) Sindre Mehus
*/
package org.airsonic.player.service;

import org.airsonic.player.service.upnp.ApacheUpnpServiceConfiguration;
import org.airsonic.player.service.upnp.CustomContentDirectory;
import org.airsonic.player.service.upnp.MSMediaReceiverRegistrarService;
import org.airsonic.player.util.FileUtil;
import org.fourthline.cling.DefaultUpnpServiceConfiguration;
import org.fourthline.cling.UpnpService;
import org.fourthline.cling.UpnpServiceImpl;
import org.fourthline.cling.binding.annotations.AnnotationLocalServiceBinder;
Expand All @@ -36,13 +36,6 @@
import org.fourthline.cling.support.model.ProtocolInfos;
import org.fourthline.cling.support.model.dlna.DLNAProfiles;
import org.fourthline.cling.support.model.dlna.DLNAProtocolInfo;
import org.fourthline.cling.transport.impl.apache.StreamClientConfigurationImpl;
import org.fourthline.cling.transport.impl.apache.StreamClientImpl;
import org.fourthline.cling.transport.impl.apache.StreamServerConfigurationImpl;
import org.fourthline.cling.transport.impl.apache.StreamServerImpl;
import org.fourthline.cling.transport.spi.NetworkAddressFactory;
import org.fourthline.cling.transport.spi.StreamClient;
import org.fourthline.cling.transport.spi.StreamServer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
Expand All @@ -51,7 +44,9 @@

import jakarta.annotation.PostConstruct;

import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
Expand All @@ -67,9 +62,14 @@ public class UPnPService {

private static final Logger LOG = LoggerFactory.getLogger(UPnPService.class);

private final static int MIN_ADVERTISEMENT_AGE_SECONDS = 60 * 60 * 24;

@Autowired
private SettingsService settingsService;

@Autowired
private VersionService versionService;

private UpnpService upnpService;

@Autowired
Expand Down Expand Up @@ -103,20 +103,15 @@ public void ensureServiceStarted() {

public void ensureServiceStopped() {
running.getAndUpdate(bo -> {
if (bo) {
if (upnpService != null) {
LOG.info("Disabling UPnP/DLNA media server");
upnpService.getRegistry().removeAllLocalDevices();
System.err.println("Shutting down UPnP service...");
upnpService.shutdown();
System.err.println("Shutting down UPnP service - Done!");
}
return false;
} else {
return false;
if (upnpService != null && bo) {
LOG.info("Disabling UPnP/DLNA media server");
upnpService.getRegistry().removeAllLocalDevices();
LOG.info("Shutting down UPnP service...");
upnpService.shutdown();
LOG.info("Shutting down UPnP service - Done!");
}
return false;
});

}

private void startService() {
Expand All @@ -132,7 +127,8 @@ private void startService() {
private synchronized void createService() {
upnpService = new UpnpServiceImpl(new ApacheUpnpServiceConfiguration(settingsService.getUPnpPort()));

// Asynch search for other devices (most importantly UPnP-enabled routers for port-mapping)
// Asynch search for other devices (most importantly UPnP-enabled routers for
// port-mapping)
upnpService.getControlPoint().search();

}
Expand All @@ -153,26 +149,9 @@ public void setMediaServerEnabled(boolean enabled) {

private LocalDevice createMediaServerDevice() throws Exception {

String serverName = settingsService.getDlnaServerName();
String serverId = settingsService.getDlnaServerId();
if (serverId == null) {
serverId = UUID.randomUUID().toString();
settingsService.setDlnaServerId(serverId);
}
DeviceIdentity identity = new DeviceIdentity(UDN.valueOf(serverId));
DeviceType type = new UDADeviceType("MediaServer", 1);

// TODO: DLNACaps

DeviceDetails details = new DeviceDetails(serverName, new ManufacturerDetails(serverName),
new ModelDetails(serverName),
new DLNADoc[]{new DLNADoc("DMS", DLNADoc.Version.V1_5)}, null);

InputStream in = getClass().getResourceAsStream("logo-512.png");
Icon icon = new Icon("image/png", 512, 512, 32, "logo-512", in);
FileUtil.closeQuietly(in);

LocalService<CustomContentDirectory> contentDirectoryservice = new AnnotationLocalServiceBinder().read(CustomContentDirectory.class);
@SuppressWarnings("unchecked")
LocalService<CustomContentDirectory> contentDirectoryservice = new AnnotationLocalServiceBinder()
.read(CustomContentDirectory.class);
contentDirectoryservice.setManager(new DefaultServiceManager<CustomContentDirectory>(contentDirectoryservice) {

@Override
Expand All @@ -193,25 +172,61 @@ protected CustomContentDirectory createServiceInstance() {
}
}

LocalService<ConnectionManagerService> connetionManagerService = new AnnotationLocalServiceBinder().read(ConnectionManagerService.class);
connetionManagerService.setManager(new DefaultServiceManager<ConnectionManagerService>(connetionManagerService) {
@Override
protected ConnectionManagerService createServiceInstance() {
return new ConnectionManagerService(protocols, null);
}
});
@SuppressWarnings("unchecked")
LocalService<ConnectionManagerService> connetionManagerService = new AnnotationLocalServiceBinder()
.read(ConnectionManagerService.class);
connetionManagerService
.setManager(new DefaultServiceManager<ConnectionManagerService>(connetionManagerService) {
@Override
protected ConnectionManagerService createServiceInstance() {
return new ConnectionManagerService(protocols, null);
}
});

// For compatibility with Microsoft
LocalService<MSMediaReceiverRegistrarService> receiverService = new AnnotationLocalServiceBinder().read(MSMediaReceiverRegistrarService.class);
@SuppressWarnings("unchecked")
LocalService<MSMediaReceiverRegistrarService> receiverService = new AnnotationLocalServiceBinder()
.read(MSMediaReceiverRegistrarService.class);
receiverService.setManager(new DefaultServiceManager<>(receiverService, MSMediaReceiverRegistrarService.class));

return new LocalDevice(identity, type, details, new Icon[]{icon}, new LocalService[]{contentDirectoryservice, connetionManagerService, receiverService});
Icon icon = null;
try (InputStream in = getClass().getResourceAsStream("logo-512.png")) {
icon = new Icon("image/png", 512, 512, 32, "logo-512", in);
} catch (IOException e) {
throw new RuntimeException(e);
}

String serverName = settingsService.getDlnaServerName();
String serverId = settingsService.getDlnaServerId();
String serialNumber = versionService.getLocalBuildNumber();
if (serverId == null) {
serverId = UUID.randomUUID().toString();
settingsService.setDlnaServerId(serverId);
}

// TODO: DLNACaps
DLNADoc[] dlnaDocs = new DLNADoc[] { new DLNADoc("DMS", DLNADoc.Version.V1_5) };
URI modelURI = URI.create("https://airsonic.github.io/");
URI manufacturerURI = URI.create("https://github.com/kagemomiji/airsonic-advanced");
URI presentaionURI = URI.create(settingsService.getDlnaBaseLANURL());
ManufacturerDetails manufacturerDetails = new ManufacturerDetails(serverName, modelURI);
ModelDetails modelDetails = new ModelDetails(serverName, null, versionService.getLocalVersion().toString(),
manufacturerURI);
DeviceDetails details = new DeviceDetails(serverName, manufacturerDetails, modelDetails, serialNumber, null,
presentaionURI, dlnaDocs, null);
DeviceIdentity identity = new DeviceIdentity(UDN.uniqueSystemIdentifier(serverName),
MIN_ADVERTISEMENT_AGE_SECONDS);
DeviceType type = new UDADeviceType("MediaServer", 1);

return new LocalDevice(identity, type, details, new Icon[] { icon },
new LocalService[] { contentDirectoryservice, connetionManagerService, receiverService });
}

public List<String> getSonosControllerHosts() {
ensureServiceStarted();
List<String> result = new ArrayList<String>();
for (Device device : upnpService.getRegistry().getDevices(new DeviceType("schemas-upnp-org", "ZonePlayer"))) {
for (Device<?, ?, ?> device : upnpService.getRegistry()
.getDevices(new DeviceType("schemas-upnp-org", "ZonePlayer"))) {
if (device instanceof RemoteDevice) {
URL descriptorURL = ((RemoteDevice) device).getIdentity().getDescriptorURL();
if (descriptorURL != null) {
Expand All @@ -221,36 +236,4 @@ public List<String> getSonosControllerHosts() {
}
return result;
}

public UpnpService getUpnpService() {
return upnpService;
}

public void setSettingsService(SettingsService settingsService) {
this.settingsService = settingsService;
}

public void setCustomContentDirectory(CustomContentDirectory customContentDirectory) {
this.dispatchingContentDirectory = customContentDirectory;
}

/**
* Note the different packages on similarly named classes from the parent
*
*/
public static class ApacheUpnpServiceConfiguration extends DefaultUpnpServiceConfiguration {
public ApacheUpnpServiceConfiguration(int streamListenPort) {
super(streamListenPort);
}

@Override
public StreamClient<?> createStreamClient() {
return new StreamClientImpl(new StreamClientConfigurationImpl(getSyncProtocolExecutorService()));
}

@Override
public StreamServer<?> createStreamServer(NetworkAddressFactory networkAddressFactory) {
return new StreamServerImpl(new StreamServerConfigurationImpl(networkAddressFactory.getStreamListenPort()));
}
}
}
Loading

0 comments on commit d35a121

Please sign in to comment.