Skip to content

Commit

Permalink
Impersonate works nicely woopy
Browse files Browse the repository at this point in the history
  • Loading branch information
Sigve Røkenes committed Sep 15, 2023
1 parent 90beb06 commit 557ec98
Show file tree
Hide file tree
Showing 8 changed files with 92 additions and 20 deletions.
13 changes: 11 additions & 2 deletions backend/root/custom_classes/middlewares.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import logging
import secrets
from contextvars import ContextVar
from tokenize import Token

from django.http import HttpRequest, HttpResponse

Expand Down Expand Up @@ -56,14 +57,22 @@ def __call__(self, request: HttpRequest) -> HttpResponse:
impersonate = request.get_signed_cookie('impersonated_user_id', default=None)
if impersonate is not None:
from samfundet.models import User
from django.middleware.csrf import get_token
request.user = User.objects.get(id=int(impersonate))
print("EYOO DUDE YOURE NOT YOURSELF")
request._force_auth_user = request.user
request._force_auth_token = get_token(request.user)
print(f"EYOO DUDE YOURE NOT YOURSELF '{request.user.username}'")
except:
pass

response = self.get_response(request)

if hasattr(response, 'requested_impersonate_user'):
response.set_signed_cookie('impersonated_user_id', request.user.id)
impersonate_user_id = response.requested_impersonate_user
if impersonate_user_id is not None:
response.set_signed_cookie('impersonated_user_id', impersonate_user_id)
print(f"Now impersonating {impersonate_user_id}")
else:
response.delete_cookie('impersonated_user_id')

return response
11 changes: 7 additions & 4 deletions backend/samfundet/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -292,11 +292,13 @@ def post(self, request: Request) -> Response:
login(request=request, user=user)
new_csrf_token = get_token(request=request)

return Response(
response = Response(
status=status.HTTP_202_ACCEPTED,
data=new_csrf_token,
headers={XCSRFTOKEN: new_csrf_token},
)
response.requested_impersonate_user = None
return response


@method_decorator(csrf_protect, 'dispatch')
Expand All @@ -309,7 +311,9 @@ def post(self, request: Request) -> Response:
return Response(status=status.HTTP_400_BAD_REQUEST)

logout(request)
return Response(status=status.HTTP_200_OK)
response = Response(status=status.HTTP_200_OK)
response.requested_impersonate_user = None
return response


@method_decorator(csrf_protect, 'dispatch')
Expand Down Expand Up @@ -348,9 +352,8 @@ class ImpersonateView(APIView):
permission_classes = [IsAuthenticated] # TODO authentication check

def post(self, request: Request) -> Response:
user_id = int(request.data.get('user_id')) if hasattr(request, 'user_id') else None
response = Response(status=200)
response.requested_impersonate_user = user_id
response.requested_impersonate_user = request.data.get('user_id', None)
return response


Expand Down
1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"path-to-regexp": "^6.2.1",
"postcss": "^8.4.18",
"react": "^18.2.0",
"react-cookie": "^6.1.1",
"react-dom": "^18.2.0",
"react-helmet-async": "^1.3.0",
"react-loading-skeleton": "^3.1.0",
Expand Down
24 changes: 22 additions & 2 deletions frontend/src/Components/Navbar/Navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@ import { Button, Link, NotificationBadge, ThemeSwitch } from '~/Components';
import { NavbarItem } from '~/Components/Navbar/components';
import { HamburgerMenu } from '~/Components/Navbar/components/HamburgerMenu';
import { useGlobalContext } from '~/GlobalContextProvider';
import { logout } from '~/api';
import { impersonateUser, logout } from '~/api';
import { englishFlag, logoBlack, logoWhite, norwegianFlag } from '~/assets';
import { useDesktop, useIsDarkTheme, useScrollY } from '~/hooks';
import { STATUS } from '~/http_status_codes';
import { KEY, LANGUAGES } from '~/i18n/constants';
import { ROUTES } from '~/routes';
import { useCookies } from 'react-cookie';
import styles from './Navbar.module.scss';

const scrollDistanceForOpaque = 30;
Expand All @@ -25,6 +26,7 @@ export function Navbar() {
const { user, setUser } = useAuthContext();
const navigate = useNavigate();
const isDesktop = useDesktop();
const [cookies, setCookie, removeCookie] = useCookies();

// Each NavbarItem can have a dropdown menu.
// We want only one of them to be extended at any time, therefore this parent component
Expand Down Expand Up @@ -136,12 +138,30 @@ export function Navbar() {
</div>
);

const isImpersonate = cookies.hasOwnProperty('impersonated_user_id');
const userDropdownLinks = (
<>
<Link url={ROUTES.frontend.admin} className={styles.navbar_dropdown_link}>
<Icon icon="material-symbols:settings" />
{t(KEY.control_panel_title)}
</Link>
{isImpersonate && (
<button
type="button"
className={classNames(styles.navbar_dropdown_link, styles.navbar_logout_button)}
onClick={() => {
impersonateUser(undefined)
.then((response) => {
window.location.reload();
})
.catch(console.error);
setIsMobileNavigation(false);
}}
>
<Icon icon="ri:spy-fill" />
Stop Agent Mode
</button>
)}
<button
type="button"
className={classNames(styles.navbar_dropdown_link, styles.navbar_logout_button)}
Expand Down Expand Up @@ -170,7 +190,7 @@ export function Navbar() {
expandedDropdown={expandedDropdown}
route={'#'}
label={user.username}
icon="material-symbols:person"
icon={isImpersonate ? 'mdi:eye' : 'material-symbols:person'}
dropdownLinks={userDropdownLinks}
/>
</div>
Expand Down
9 changes: 8 additions & 1 deletion frontend/src/Pages/AdminPage/AdminPage.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { Icon } from '@iconify/react';
import { useAuthContext } from '~/AuthContext';
import { ToggleSwitch } from '~/Components';
import { Button, ToggleSwitch } from '~/Components';
import { Page } from '~/Components/Page';
import { useGlobalContext } from '~/GlobalContextProvider';
import styles from './AdminPage.module.scss';
import { WISEWORDS } from './data';
import { ROUTES } from '~/routes';
import { Link } from 'react-router-dom';

export function AdminPage() {
const { user } = useAuthContext();
Expand Down Expand Up @@ -33,6 +35,11 @@ export function AdminPage() {
<div className={styles.label}>Mouse Trail</div>
<ToggleSwitch checked={isMouseTrail} onChange={toggleMouseTrail} />
</div>
<br />
<br />
<Link to={ROUTES.frontend.admin_impersonate}>
<Button>Stjel Identitet</Button>
</Link>
</div>
</Page>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,7 @@ export function ImpersonateUserAdminPage() {
function impersonate(user: UserDto) {
impersonateUser(user)
.then((ok) => {
if (ok) {
getUser()
.then((user) => auth.setUser(user))
.catch(console.error);
alert('nice, middleware is good, TODO proper handling in frontend');
}
window.location.reload();
})
.catch((err) => {
alert(JSON.stringify(err));
Expand All @@ -72,7 +67,7 @@ export function ImpersonateUserAdminPage() {
<InputField<string> inputClassName={styles.inputClass} placeholder={'Search...'} onChange={setQuery} />
<div className={styles.userList}>
{displayUsers.map((u) => (
<button className={styles.userItem} onClick={() => impersonate(u)}>
<button className={styles.userItem} onClick={() => impersonate(u)} key={u.id}>
<span>{verboseUserName(u)}</span>
<span className={styles.email}>{u.email}</span>
</button>
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,9 @@ export async function getUser(): Promise<UserDto> {
return response.data;
}

export async function impersonateUser(user: UserDto): Promise<boolean> {
export async function impersonateUser(user?: UserDto): Promise<boolean> {
const url = BACKEND_DOMAIN + ROUTES.backend.samfundet__impersonate;
const response = await axios.post(url, { user_id: user.id }, { withCredentials: true });
const response = await axios.post(url, { user_id: user?.id }, { withCredentials: true });
return response.status == 200;
}

Expand Down
41 changes: 39 additions & 2 deletions frontend/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3501,6 +3501,11 @@
dependencies:
"@types/node" "*"

"@types/cookie@^0.5.1":
version "0.5.2"
resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.5.2.tgz#9bf9d62c838c85a07c92fdf2334c2c14fd9c59a9"
integrity sha512-DBpRoJGKJZn7RY92dPrgoMew8xCWc2P71beqsjyhEI/Ds9mOyVmBwtekyfhpwFIVt1WrxTonFifiOZ62V8CnNA==

"@types/debug@^4.0.0":
version "4.1.7"
resolved "https://registry.npmjs.org/@types/debug/-/debug-4.1.7.tgz"
Expand Down Expand Up @@ -3588,6 +3593,14 @@
dependencies:
"@types/unist" "*"

"@types/hoist-non-react-statics@^3.3.1":
version "3.3.1"
resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f"
integrity sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==
dependencies:
"@types/react" "*"
hoist-non-react-statics "^3.3.0"

"@types/html-minifier-terser@^5.0.0":
version "5.1.2"
resolved "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-5.1.2.tgz"
Expand Down Expand Up @@ -6077,7 +6090,7 @@ [email protected]:
resolved "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz"
integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==

[email protected]:
[email protected], cookie@^0.5.0:
version "0.5.0"
resolved "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz"
integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==
Expand Down Expand Up @@ -8945,6 +8958,13 @@ hmac-drbg@^1.0.1:
minimalistic-assert "^1.0.0"
minimalistic-crypto-utils "^1.0.1"

hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2:
version "3.3.2"
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==
dependencies:
react-is "^16.7.0"

hoopy@^0.1.4:
version "0.1.4"
resolved "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz"
Expand Down Expand Up @@ -13525,6 +13545,15 @@ react-app-polyfill@^3.0.0:
regenerator-runtime "^0.13.9"
whatwg-fetch "^3.6.2"

react-cookie@^6.1.1:
version "6.1.1"
resolved "https://registry.yarnpkg.com/react-cookie/-/react-cookie-6.1.1.tgz#af61c82ea7f41119d3cf8fd3534f8b6c0408f350"
integrity sha512-fuFRpf8LH6SfmVMowDUIRywJF5jAUDUWrm0EI5VdXfTl5bPcJ7B0zWbuYpT0Tvikx7Gs18MlvAT+P+744dUz2g==
dependencies:
"@types/hoist-non-react-statics" "^3.3.1"
hoist-non-react-statics "^3.3.2"
universal-cookie "^6.0.0"

react-dev-utils@^12.0.1:
version "12.0.1"
resolved "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz"
Expand Down Expand Up @@ -13652,7 +13681,7 @@ [email protected], react-is@^17.0.1:
resolved "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz"
integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==

react-is@^16.13.1:
react-is@^16.13.1, react-is@^16.7.0:
version "16.13.1"
resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
Expand Down Expand Up @@ -16183,6 +16212,14 @@ unist-util-visit@^4.0.0:
unist-util-is "^5.0.0"
unist-util-visit-parents "^5.1.1"

universal-cookie@^6.0.0:
version "6.1.1"
resolved "https://registry.yarnpkg.com/universal-cookie/-/universal-cookie-6.1.1.tgz#2d619e21804c93b39ff0c77fe47dafd7a492346d"
integrity sha512-33S9x3CpdUnnjwTNs2Fgc41WGve2tdLtvaK2kPSbZRc5pGpz2vQFbRWMxlATsxNNe/Cy8SzmnmbuBM85jpZPtA==
dependencies:
"@types/cookie" "^0.5.1"
cookie "^0.5.0"

universalify@^0.2.0:
version "0.2.0"
resolved "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz"
Expand Down

0 comments on commit 557ec98

Please sign in to comment.