/* * Copyright (C) 2008-2023, Juick * * This program 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. * * This program 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 com.juick.www.api; import com.fasterxml.jackson.annotation.JsonProperty; import com.juick.model.User; import com.juick.service.UserService; import com.juick.www.WebApp; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.MediaType; import org.springframework.security.oauth2.core.AuthorizationGrantType; import org.springframework.security.oauth2.core.ClientAuthenticationMethod; import org.springframework.security.oauth2.server.authorization.client.RegisteredClient; import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository; import org.springframework.security.oauth2.server.authorization.settings.ClientSettings; import org.springframework.web.bind.annotation.*; import javax.inject.Inject; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.UUID; @RestController public class Mastodon { @Value("${web_domain:localhost}") private String domain; @Inject WebApp webApp; @Inject UserService userService; @Inject RegisteredClientRepository registeredClientRepository; public record ApplicationRequest(@JsonProperty("client_name") String clientName, @JsonProperty("redirect_uris") String redirectUris, @JsonProperty("scopes") String scopes) { } public record ApplicationResponse(String id, @JsonProperty("client_name") String name, @JsonProperty("redirect_uri") String redirectUri, @JsonProperty("client_id") String clientId, @JsonProperty("client_secret") String clientSecret) { } public record CredentialAccount(String id, String username, String acct, @JsonProperty("display_name") String displayName, @JsonProperty("followers_count") Integer followersCount, @JsonProperty("following_count") Integer followingCount, String avatar) { } private Collection parseScopes(String s) { return s != null ? Arrays.asList(s.split(" ")) : Collections.emptyList(); } @PostMapping(value = "/api/v1/apps", consumes = { MediaType.APPLICATION_JSON_VALUE }) public ApplicationResponse apps(@RequestBody ApplicationRequest application) { return apps(application.clientName(), application.redirectUris(), application.scopes()); } @PostMapping(value = "/api/v1/apps", consumes = { MediaType.APPLICATION_FORM_URLENCODED_VALUE, MediaType.MULTIPART_FORM_DATA_VALUE }) public ApplicationResponse apps(@RequestParam("client_name") String clientName, @RequestParam("redirect_uris") String redirectUris, @RequestParam("scopes") String scopes) { var secret = UUID.randomUUID().toString(); RegisteredClient registeredClient = RegisteredClient.withId(UUID.randomUUID().toString()) .clientId(UUID.randomUUID().toString()) .clientSecret("{noop}" + secret) .clientName(clientName) .clientAuthenticationMethods(coll -> coll.addAll(List.of( ClientAuthenticationMethod.CLIENT_SECRET_POST, ClientAuthenticationMethod.CLIENT_SECRET_BASIC ))) .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) .redirectUri(redirectUris) .scopes((coll) -> coll.addAll(parseScopes(scopes))) .clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build()).build(); registeredClientRepository.save(registeredClient); return new ApplicationResponse( registeredClient.getId(), registeredClient.getClientName(), String.join(",", registeredClient.getRedirectUris()), registeredClient.getClientId(), secret ); } public record Instance(String domain) {} @GetMapping("/api/v1/instance") public Instance getInstance() { return new Instance(domain); } @GetMapping("/api/v1/accounts/verify_credentials") public CredentialAccount account(@ModelAttribute User visitor) { return new CredentialAccount( String.valueOf(visitor.getUid()), visitor.getName(), visitor.getName(), visitor.getFullName(), userService.getUserReaders(visitor.getUid()).size(), userService.getUserFriends(visitor.getUid()).size(), webApp.getAvatarUrl(visitor) ); } }