merge repository heads

This commit is contained in:
Sebastian Sdorra
2018-07-24 14:48:20 +02:00
6 changed files with 151 additions and 26 deletions

View File

@@ -1,9 +1,8 @@
//@flow
import React from "react";
import type { Me } from "../types/me";
type Props = {
me?: Me
me?: string
};
class Footer extends React.Component<Props> {
@@ -15,7 +14,7 @@ class Footer extends React.Component<Props> {
return (
<footer className="footer">
<div className="container is-centered">
<p className="has-text-centered">{me.username}</p>
<p className="has-text-centered">{me}</p>
</div>
</footer>
);

View File

@@ -18,6 +18,7 @@ type Props = {
error: Error,
loading: boolean,
authenticated?: boolean,
displayName: string,
t: string => string,
fetchMe: () => void
};
@@ -28,7 +29,7 @@ class App extends Component<Props> {
}
render() {
const { entry, loading, error, t, authenticated } = this.props;
const { loading, error, authenticated, displayName, t } = this.props;
let content;
const navigation = authenticated ? <PrimaryNavigation /> : "";
@@ -50,7 +51,7 @@ class App extends Component<Props> {
<div className="App">
<Header>{navigation}</Header>
{content}
<Footer me={entry} />
<Footer me={displayName} />
</div>
);
}
@@ -64,10 +65,17 @@ const mapDispatchToProps = (dispatch: any) => {
const mapStateToProps = state => {
let mapped = state.auth.me || {};
let displayName;
if (state.auth.login) {
mapped.authenticated = state.auth.login.authenticated;
}
return mapped;
if (state.auth.me && state.auth.me.entry) {
displayName = state.auth.me.entry.entity.displayName;
}
return {
...mapped,
displayName
};
};
export default withRouter(

View File

@@ -1,4 +0,0 @@
// @flow
export type Me = {
username: string
};

View File

@@ -25,6 +25,7 @@ export const DELETE_USER_SUCCESS = "scm/users/DELETE_SUCCESS";
export const DELETE_USER_FAILURE = "scm/users/DELETE_FAILURE";
const USERS_URL = "users";
const USER_URL = "users/";
const CONTENT_TYPE_USER = "application/vnd.scmm-user+json;v=2";

View File

@@ -1,34 +1,52 @@
package sonia.scm.api.v2.resources;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import com.webcohesion.enunciate.metadata.rs.ResponseCode;
import com.webcohesion.enunciate.metadata.rs.StatusCodes;
import com.webcohesion.enunciate.metadata.rs.TypeHint;
import org.apache.shiro.SecurityUtils;
import sonia.scm.user.User;
import sonia.scm.user.UserException;
import sonia.scm.user.UserManager;
import sonia.scm.web.VndMediaType;
import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Request;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
@Path(MeResource.ME_PATH_V2)
public class MeResource {
static final String ME_PATH_V2 = "v2/me/";
private final UserToUserDtoMapper userToDtoMapper;
private final IdResourceManagerAdapter<User, UserDto, UserException> adapter;
@Inject
public MeResource(UserToUserDtoMapper userToDtoMapper, UserManager manager) {
this.userToDtoMapper = userToDtoMapper;
this.adapter = new IdResourceManagerAdapter<>(manager, User.class);
}
/**
* Returns the currently logged in user or a 401 if user is not logged in
*/
@GET
@Produces(VndMediaType.ME)
public Response get() {
MeDto meDto = new MeDto((String) SecurityUtils.getSubject().getPrincipals().getPrimaryPrincipal());
return Response.ok(meDto).build();
}
@Path("")
@Produces(VndMediaType.USER)
@TypeHint(UserDto.class)
@StatusCodes({
@ResponseCode(code = 200, condition = "success"),
@ResponseCode(code = 401, condition = "not authenticated / invalid credentials"),
@ResponseCode(code = 500, condition = "internal server error")
})
public Response get(@Context Request request, @Context UriInfo uriInfo) {
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
class MeDto {
String username;
String id = (String) SecurityUtils.getSubject().getPrincipals().getPrimaryPrincipal();
return adapter.get(id, userToDtoMapper::map);
}
}

View File

@@ -0,0 +1,103 @@
package sonia.scm.api.v2.resources;
import com.github.sdorra.shiro.ShiroRule;
import com.github.sdorra.shiro.SubjectAware;
import com.google.common.io.Resources;
import org.apache.shiro.authc.credential.PasswordService;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.jboss.resteasy.core.Dispatcher;
import org.jboss.resteasy.mock.MockDispatcherFactory;
import org.jboss.resteasy.mock.MockHttpRequest;
import org.jboss.resteasy.mock.MockHttpResponse;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import sonia.scm.PageResult;
import sonia.scm.user.User;
import sonia.scm.user.UserException;
import sonia.scm.user.UserManager;
import sonia.scm.web.VndMediaType;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.core.UriInfo;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import static java.util.Collections.singletonList;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.*;
import static org.mockito.MockitoAnnotations.initMocks;
@SubjectAware(
// username = "trillian",
// password = "secret",
configuration = "classpath:sonia/scm/repository/shiro.ini"
)
public class MeResourceTest {
@Rule
public ShiroRule shiro = new ShiroRule();
private Dispatcher dispatcher = MockDispatcherFactory.createDispatcher();
@Mock
private UriInfo uriInfo;
@Mock
private UriInfoStore uriInfoStore;
@Mock
private UserManager userManager;
@InjectMocks
private UserToUserDtoMapperImpl userToDtoMapper;
private ArgumentCaptor<User> userCaptor = ArgumentCaptor.forClass(User.class);
@Before
public void prepareEnvironment() throws IOException, UserException {
initMocks(this);
createDummyUser("trillian");
doNothing().when(userManager).create(userCaptor.capture());
doNothing().when(userManager).modify(userCaptor.capture());
doNothing().when(userManager).delete(userCaptor.capture());
MeResource meResource = new MeResource(userToDtoMapper, userManager);
dispatcher.getRegistry().addSingletonResource(meResource);
when(uriInfo.getBaseUri()).thenReturn(URI.create("/"));
when(uriInfoStore.get()).thenReturn(uriInfo);
}
@Test
@SubjectAware(username = "trillian", password = "secret")
public void shouldReturnCurrentlyAuthenticatedUser() throws URISyntaxException {
MockHttpRequest request = MockHttpRequest.get("/" + MeResource.ME_PATH_V2);
request.accept(VndMediaType.USER);
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
assertEquals(HttpServletResponse.SC_OK, response.getStatus());
assertTrue(response.getContentAsString().contains("\"name\":\"trillian\""));
assertTrue(response.getContentAsString().contains("\"password\":\"__dummypassword__\""));
assertTrue(response.getContentAsString().contains("\"self\":{\"href\":\"/v2/users/trillian\"}"));
assertTrue(response.getContentAsString().contains("\"delete\":{\"href\":\"/v2/users/trillian\"}"));
}
private User createDummyUser(String name) {
User user = new User();
user.setName(name);
user.setPassword("secret");
user.setCreationDate(System.currentTimeMillis());
when(userManager.get(name)).thenReturn(user);
return user;
}
}