Skip to content

Commit

Permalink
Merge pull request #13048 from gzsombor/protect-users
Browse files Browse the repository at this point in the history
Fixes #12374: Protect user api
  • Loading branch information
pascalgrimaud authored Dec 18, 2020
2 parents 194009e + ddb83bf commit 0be6e0f
Show file tree
Hide file tree
Showing 28 changed files with 981 additions and 402 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ describe('Component Tests', () => {
fakeAsync(() => {
// GIVEN
const headers = new HttpHeaders().append('link', 'link;link');
spyOn(service, 'query').and.returnValue(
spyOn(service, 'queryAsAdmin').and.returnValue(
of(
new HttpResponse({
body: [new User(<%- tsKeyId %>)],
Expand All @@ -91,7 +91,7 @@ describe('Component Tests', () => {
tick(); // simulate async

// THEN
expect(service.query).toHaveBeenCalled();
expect(service.queryAsAdmin).toHaveBeenCalled();
expect(comp.users?.[0]).toEqual(jasmine.objectContaining({ id: <%- tsKeyId %> }));
})
));
Expand All @@ -104,7 +104,7 @@ describe('Component Tests', () => {
// GIVEN
const headers = new HttpHeaders().append('link', 'link;link');
const user = new User(<%- tsKeyId %>);
spyOn(service, 'query').and.returnValue(
spyOn(service, 'queryAsAdmin').and.returnValue(
of(
new HttpResponse({
body: [user],
Expand All @@ -120,7 +120,7 @@ describe('Component Tests', () => {

// THEN
expect(service.update).toHaveBeenCalledWith({ ...user, activated: true });
expect(service.query).toHaveBeenCalled();
expect(service.queryAsAdmin).toHaveBeenCalled();
expect(comp.users?.[0]).toEqual(jasmine.objectContaining({ id: <%- tsKeyId %> }));
})
));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export class UserManagementComponent implements OnInit {
loadAll(): void {
this.isLoading = true;
this.userService
.query(<% if (databaseType !== 'cassandra') { %>{
.queryAsAdmin(<% if (databaseType !== 'cassandra') { %>{
page: this.page - 1,
size: this.itemsPerPage,
sort: this.sort(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,15 @@ export class User implements IUser {
public password?: string
) {}
}

export interface IPublicUser {
id?: <%= idType %>;
name?: string;
}

export class PublicUser implements IPublicUser {
constructor(
public id?: <%= idType %>,
public login?: string,
) {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ describe('Service Tests', () => {
service.find('user').subscribe();

const req = httpMock.expectOne({ method: 'GET' });
const resourceUrl = SERVER_API_URL + '<%- apiUaaPath %>api/users';
const resourceUrl = SERVER_API_URL + '<%- apiUaaPath %>api/admin/users';
expect(req.request.url).toEqual(`${resourceUrl}/user`);
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,44 +23,50 @@ import { Observable<% if (authenticationType !== 'oauth2' && databaseType !== 's
import { SERVER_API_URL } from 'app/app.constants';
import { createRequestOption } from 'app/core/request/request-util';
import { Pagination } from 'app/core/request/request.model';
import { IUser } from './user.model';
import { IPublicUser, IUser } from './user.model';
<%_ if (authenticationType !== 'oauth2' && databaseType !== 'sql' && databaseType !== 'mongodb' && databaseType !== 'couchbase') { _%>
import { Authority } from 'app/config/authority.constants';
<%_ } _%>

@Injectable({ providedIn: 'root' })
export class UserService {
public resourceUrl = SERVER_API_URL + '<%- apiUaaPath %>api/users';
public userManagementUrl = SERVER_API_URL + '<%- apiUaaPath %>api/admin/users';

constructor(private http: HttpClient) {}

<%_ if (authenticationType !== 'oauth2') { _%>
create(user: IUser): Observable<IUser> {
return this.http.post<IUser>(this.resourceUrl, user);
return this.http.post<IUser>(this.userManagementUrl, user);
}

update(user: IUser): Observable<IUser> {
return this.http.put<IUser>(this.resourceUrl, user);
return this.http.put<IUser>(this.userManagementUrl, user);
}

find(login: string): Observable<IUser> {
return this.http.get<IUser>(`${this.resourceUrl}/${login}`);
return this.http.get<IUser>(`${this.userManagementUrl}/${login}`);
}
<%_ } _%>

query(req?: Pagination): Observable<HttpResponse<IUser[]>> {
query(req?: Pagination): Observable<HttpResponse<IPublicUser[]>> {
const options = createRequestOption(req);
return this.http.get<IUser[]>(this.resourceUrl, { params: options, observe: 'response' });
return this.http.get<IPublicUser[]>(this.resourceUrl, { params: options, observe: 'response' });
}

queryAsAdmin(req?: Pagination): Observable<HttpResponse<IUser[]>> {
const options = createRequestOption(req);
return this.http.get<IUser[]>(this.userManagementUrl, { params: options, observe: 'response' });
}

<%_ if (authenticationType !== 'oauth2') { _%>
delete(login: string): Observable<{}> {
return this.http.delete(`${this.resourceUrl}/${login}`);
return this.http.delete(`${this.userManagementUrl}/${login}`);
}

authorities(): Observable<string[]> {
<%_ if (databaseType === 'sql' || databaseType === 'mongodb' || databaseType === 'couchbase') { _%>
return this.http.get<string[]>(SERVER_API_URL + '<%- apiUaaPath %>api/users/authorities');
return this.http.get<string[]>(SERVER_API_URL + '<%- apiUaaPath %>api/authorities');
<%_ } else { _%>
return of([Authority.ADMIN, Authority.USER]);
<%_ } _%>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { IUser, defaultValue } from 'app/shared/model/user.model';
export const ACTION_TYPES = {
FETCH_ROLES: 'userManagement/FETCH_ROLES',
FETCH_USERS: 'userManagement/FETCH_USERS',
FETCH_USERS_AS_ADMIN: 'userManagement/FETCH_USERS_AS_ADMIN',
FETCH_USER: 'userManagement/FETCH_USER',
CREATE_USER: 'userManagement/CREATE_USER',
UPDATE_USER: 'userManagement/UPDATE_USER',
Expand All @@ -43,7 +44,7 @@ const initialState = {
totalItems: 0
};

export type UserManagementState = Readonly<typeof initialState>;
export type UserManagementState = Readonly<typeof initialState>;

// Reducer
export default (state: UserManagementState = initialState, action): UserManagementState => {
Expand All @@ -53,6 +54,7 @@ export default (state: UserManagementState = initialState, action): UserManageme
...state
};
case REQUEST(ACTION_TYPES.FETCH_USERS):
case REQUEST(ACTION_TYPES.FETCH_USERS_AS_ADMIN):
case REQUEST(ACTION_TYPES.FETCH_USER):
return {
...state,
Expand All @@ -70,6 +72,7 @@ export default (state: UserManagementState = initialState, action): UserManageme
updating: true
};
case FAILURE(ACTION_TYPES.FETCH_USERS):
case FAILURE(ACTION_TYPES.FETCH_USERS_AS_ADMIN):
case FAILURE(ACTION_TYPES.FETCH_USER):
case FAILURE(ACTION_TYPES.FETCH_ROLES):
case FAILURE(ACTION_TYPES.CREATE_USER):
Expand All @@ -88,6 +91,7 @@ export default (state: UserManagementState = initialState, action): UserManageme
authorities: action.payload.data
};
case SUCCESS(ACTION_TYPES.FETCH_USERS):
case SUCCESS(ACTION_TYPES.FETCH_USERS_AS_ADMIN):
return {
...state,
loading: false,
Expand Down Expand Up @@ -127,6 +131,7 @@ export default (state: UserManagementState = initialState, action): UserManageme
};

const apiUrl = '<%= apiUaaPath %>api/users';
const adminUrl = '<%= apiUaaPath %>api/admin/users';
// Actions
export const getUsers: ICrudGetAllAction<IUser> = (page, size, sort) => {
const requestUrl = `${apiUrl}${sort ? `?page=${page}&size=${size}&sort=${sort}` : ''}`;
Expand All @@ -136,13 +141,21 @@ export const getUsers: ICrudGetAllAction<IUser> = (page, size, sort) => {
};
};

export const getUsersAsAdmin: ICrudGetAllAction<IUser> = (page, size, sort) => {
const requestUrl = `${adminUrl}${sort ? `?page=${page}&size=${size}&sort=${sort}` : ''}`;
return {
type: ACTION_TYPES.FETCH_USERS_AS_ADMIN,
payload: axios.get<IUser>(requestUrl),
};
};

export const getRoles = () => ({
type: ACTION_TYPES.FETCH_ROLES,
payload: axios.get(`${apiUrl}/authorities`)
payload: axios.get(`<%= apiUaaPath %>api/authorities`)
});

export const getUser: ICrudGetAction<IUser> = id => {
const requestUrl = `${apiUrl}/${id}`;
const requestUrl = `${adminUrl}/${id}`;
return {
type: ACTION_TYPES.FETCH_USER,
payload: axios.get<IUser>(requestUrl)
Expand All @@ -152,28 +165,28 @@ export const getUser: ICrudGetAction<IUser> = id => {
export const createUser: ICrudPutAction<IUser> = user => async dispatch => {
const result = await dispatch({
type: ACTION_TYPES.CREATE_USER,
payload: axios.post(apiUrl, user)
payload: axios.post(adminUrl, user)
});
dispatch(getUsers());
dispatch(getUsersAsAdmin());
return result;
};

export const updateUser: ICrudPutAction<IUser> = user => async dispatch => {
const result = await dispatch({
type: ACTION_TYPES.UPDATE_USER,
payload: axios.put(apiUrl, user)
payload: axios.put(adminUrl, user)
});
dispatch(getUsers());
dispatch(getUsersAsAdmin());
return result;
};

export const deleteUser: ICrudDeleteAction<IUser> = id => async dispatch => {
const requestUrl = `${apiUrl}/${id}`;
const requestUrl = `${adminUrl}/${id}`;
const result = await dispatch({
type: ACTION_TYPES.DELETE_USER,
payload: axios.delete(requestUrl)
});
dispatch(getUsers());
dispatch(getUsersAsAdmin());
return result;
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { APP_DATE_FORMAT } from 'app/config/constants';
import { ITEMS_PER_PAGE } from 'app/shared/util/pagination.constants';
import { overridePaginationStateWithQueryParams } from 'app/shared/util/entity-utils';
import { getUsers, updateUser } from './user-management.reducer';
import { getUsersAsAdmin, updateUser } from './user-management.reducer';
import { IRootState } from 'app/shared/reducers';

export interface IUserManagementProps extends StateProps, DispatchProps, RouteComponentProps<any> {}
Expand All @@ -41,7 +41,7 @@ export const UserManagement = (props: IUserManagementProps) => {
const [pagination, setPagination] = useState(overridePaginationStateWithQueryParams(getSortState(props.location, ITEMS_PER_PAGE), props.location.search));

const getUsersFromProps = () => {
props.getUsers(pagination.activePage - 1, pagination.itemsPerPage, `${pagination.sort},${pagination.order}`);
props.getUsersAsAdmin(pagination.activePage - 1, pagination.itemsPerPage, `${pagination.sort},${pagination.order}`);
const endURL = `?page=${pagination.activePage}&sort=${pagination.sort},${pagination.order}`;
if (props.location.search !== endURL) {
props.history.push(`${props.location.pathname}${endURL}`);
Expand Down Expand Up @@ -230,7 +230,7 @@ const mapStateToProps = (storeState: IRootState) => ({
account: storeState.authentication.account
});

const mapDispatchToProps = { getUsers, updateUser };
const mapDispatchToProps = { getUsersAsAdmin, updateUser };

type StateProps = ReturnType<typeof mapStateToProps>;
type DispatchProps = typeof mapDispatchToProps;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { REQUEST, SUCCESS, FAILURE } from 'app/shared/reducers/action-type.util'
import userManagement, {
ACTION_TYPES,
getUsers,
getUsersAsAdmin,
getRoles,
getUser,
createUser,
Expand Down Expand Up @@ -79,7 +80,7 @@ describe('User management reducer tests', () => {
});

it('should set state to loading', () => {
testMultipleTypes([REQUEST(ACTION_TYPES.FETCH_USERS), REQUEST(ACTION_TYPES.FETCH_USER)], {}, state => {
testMultipleTypes([REQUEST(ACTION_TYPES.FETCH_USERS), REQUEST(ACTION_TYPES.FETCH_USERS_AS_ADMIN), REQUEST(ACTION_TYPES.FETCH_USER)], {}, state => {
expect(state).toMatchObject({
errorMessage: null,
updateSuccess: false,
Expand Down Expand Up @@ -107,6 +108,7 @@ describe('User management reducer tests', () => {
it('should set state to failed and put an error message in errorMessage', () => {
testMultipleTypes(
[
FAILURE(ACTION_TYPES.FETCH_USERS_AS_ADMIN),
FAILURE(ACTION_TYPES.FETCH_USERS),
FAILURE(ACTION_TYPES.FETCH_USER),
FAILURE(ACTION_TYPES.FETCH_ROLES),
Expand Down Expand Up @@ -226,6 +228,30 @@ describe('User management reducer tests', () => {
axios.post = sinon.stub().returns(Promise.resolve(resolvedObject));
axios.delete = sinon.stub().returns(Promise.resolve(resolvedObject));
});
it('dispatches FETCH_USERS_AS_ADMIN_PENDING and FETCH_USERS_AS_ADMIN_FULFILLED actions', async () => {
const expectedActions = [
{
type: REQUEST(ACTION_TYPES.FETCH_USERS_AS_ADMIN)
},
{
type: SUCCESS(ACTION_TYPES.FETCH_USERS_AS_ADMIN),
payload: resolvedObject
}
];
await store.dispatch(getUsersAsAdmin()).then(() => expect(store.getActions()).toEqual(expectedActions));
});
it('dispatches FETCH_USERS_AS_ADMIN_PENDING and FETCH_USERS_AS_ADMIN_FULFILLED actions with pagination options', async () => {
const expectedActions = [
{
type: REQUEST(ACTION_TYPES.FETCH_USERS_AS_ADMIN)
},
{
type: SUCCESS(ACTION_TYPES.FETCH_USERS_AS_ADMIN),
payload: resolvedObject
}
];
await store.dispatch(getUsersAsAdmin(1, 20, 'id,desc')).then(() => expect(store.getActions()).toEqual(expectedActions));
});

it('dispatches FETCH_USERS_PENDING and FETCH_USERS_FULFILLED actions', async () => {
const expectedActions = [
Expand Down Expand Up @@ -285,10 +311,10 @@ describe('User management reducer tests', () => {
payload: resolvedObject
},
{
type: REQUEST(ACTION_TYPES.FETCH_USERS)
type: REQUEST(ACTION_TYPES.FETCH_USERS_AS_ADMIN)
},
{
type: SUCCESS(ACTION_TYPES.FETCH_USERS),
type: SUCCESS(ACTION_TYPES.FETCH_USERS_AS_ADMIN),
payload: resolvedObject
}
];
Expand All @@ -304,10 +330,10 @@ describe('User management reducer tests', () => {
payload: resolvedObject
},
{
type: REQUEST(ACTION_TYPES.FETCH_USERS)
type: REQUEST(ACTION_TYPES.FETCH_USERS_AS_ADMIN)
},
{
type: SUCCESS(ACTION_TYPES.FETCH_USERS),
type: SUCCESS(ACTION_TYPES.FETCH_USERS_AS_ADMIN),
payload: resolvedObject
}
];
Expand All @@ -323,10 +349,10 @@ describe('User management reducer tests', () => {
payload: resolvedObject
},
{
type: REQUEST(ACTION_TYPES.FETCH_USERS)
type: REQUEST(ACTION_TYPES.FETCH_USERS_AS_ADMIN)
},
{
type: SUCCESS(ACTION_TYPES.FETCH_USERS),
type: SUCCESS(ACTION_TYPES.FETCH_USERS_AS_ADMIN),
payload: resolvedObject
}
];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export default class UserManagementService {

public retrieveAuthorities(): Promise<any> {
<%_ if (databaseType !== 'cassandra') { _%>
return axios.get('<%- apiUaaPath %>api/users/authorities');
return axios.get('<%- apiUaaPath %>api/authorities');
<%_ } else { _%>
return Promise.resolve([Authority.USER, Authority.ADMIN]);
<%_ } _%>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ describe('UserManagementEdit Component', () => {
await userManagementEdit.$nextTick();

// THEN
expect(axiosStub.get.calledWith(`api/users/authorities`)).toBeTruthy();
expect(axiosStub.get.calledWith(`api/authorities`)).toBeTruthy();
});
});

Expand Down
1 change: 1 addition & 0 deletions generators/entity/prompts.js
Original file line number Diff line number Diff line change
Expand Up @@ -1003,6 +1003,7 @@ function askForRelationship() {
{
when: response =>
response.relationshipAdd === true &&
response.otherEntityName.toLowerCase() !== 'user' &&
(response.relationshipType === 'many-to-one' ||
(response.relationshipType === 'many-to-many' && response.ownerSide === true) ||
(response.relationshipType === 'one-to-one' && response.ownerSide === true)),
Expand Down
Loading

0 comments on commit 0be6e0f

Please sign in to comment.