Skip to content

Commit

Permalink
Merge pull request #276 from steff456/fix-login
Browse files Browse the repository at this point in the history
Add logout functionality and improve login/logout design
  • Loading branch information
pierrotsmnrd authored Aug 29, 2023
2 parents f1c5885 + bceffce commit aae1a55
Show file tree
Hide file tree
Showing 10 changed files with 143 additions and 81 deletions.
3 changes: 2 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ REACT_APP_LOGIN_PAGE_URL=http://localhost:5000/conda-store/login?next=
REACT_APP_AUTH_TOKEN=
REACT_APP_STYLE_TYPE=grayscale
REACT_APP_CONTEXT=webapp
REACT_APP_SHOW_LOGIN_ICON=true
REACT_APP_SHOW_AUTH_BUTTON=true
REACT_APP_LOGOUT_PAGE_URL=http://localhost:5000/conda-store/logout?next=/
34 changes: 0 additions & 34 deletions src/components/icons/LoginIcon.tsx

This file was deleted.

1 change: 0 additions & 1 deletion src/components/icons/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
export * from "./CondaLogo";
export * from "./LoginIcon";
export * from "./GroupIconAlt";
export * from "./ArrowIcon";
export * from "./SearchIconAlt";
Expand Down
28 changes: 18 additions & 10 deletions src/docs/markdown/CONFIGURATION.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
# Configuration

The configuration for conda-store-ui, including the connection details to conda-store, can be done :
The configuration for conda-store-ui, including the connection details to conda-store, can be done :
- either at compile time, using a `.env` file.
- or at runtime, using `condaStoreConfig` variable


## At compile time, using `.env`

conda-store-ui looks for a `.env` file when packing the bundle.
conda-store-ui looks for a `.env` file when packing the bundle.
Below, you'll find the options and the listed descriptions. You are welcome to copy this configuration, otherwise, you can copy and rename the `.env.example` file provided in the repository.

Sample File:
Expand All @@ -18,23 +18,25 @@ REACT_APP_AUTH_METHOD=cookie
REACT_APP_LOGIN_PAGE_URL=http://localhost:5000/conda-store/login?next=
REACT_APP_AUTH_TOKEN=
REACT_APP_STYLE_TYPE=grayscale
REACT_APP_SHOW_LOGIN_ICON=true
REACT_APP_SHOW_AUTH_BUTTON=true
REACT_APP_LOGOUT_PAGE_URL=http://localhost:5000/conda-store/logout?next=/
```

## At runtime, using `condaStoreConfig`

When using a webpacked version of `conda-store-ui`, you might want to pass it a configuration.
In your HTML file, add the following **before** loading the react app :
When using a webpacked version of `conda-store-ui`, you might want to pass it a configuration.
In your HTML file, add the following **before** loading the react app :

```html
<script>
const condaStoreConfig = {
REACT_APP_AUTH_METHOD: "cookie",
REACT_APP_AUTH_TOKEN: "",
REACT_APP_STYLE_TYPE: "grayscale",
REACT_APP_SHOW_LOGIN_ICON: "true",
REACT_APP_SHOW_AUTH_BUTTON: "true",
REACT_APP_API_URL: "http://localhost:5000/conda-store",
REACT_APP_LOGIN_PAGE_URL: "http://localhost:5000/conda-store/login?next=",
REACT_APP_LOGOUT_PAGE_URL: "http://localhost:5000/conda-store/logout?next=/",
};
</script>
```
Expand Down Expand Up @@ -70,7 +72,13 @@ Lets users utilize a token generated by `conda-store` to authenticate to the ser
|---------------------------|--------|
|`REACT_APP_LOGIN_PAGE_URL` | string |

The URL endpoint used for authentication.
The URL endpoint used for login authentication.

| Key | Value |
|----------------------------|--------|
|`REACT_APP_LOGOUT_PAGE_URL` | string |

The URL endpoint used for logout authentication.

| Key | Value |
|------------------------|--------------------|
Expand All @@ -89,9 +97,9 @@ Options:

* `green-accent`

| Key | Value |
|---------------------------|--------|
|`REACT_APP_SHOW_LOGIN_ICON`| bool |
| Key | Value |
|----------------------------|--------|
|`REACT_APP_SHOW_AUTH_BUTTON`| bool |

Options:

Expand Down
24 changes: 24 additions & 0 deletions src/features/auth/authApiSlice.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { apiSlice } from "../api";

export const authApiSlice = apiSlice.injectEndpoints({
endpoints: builder => ({
getAuth: builder.query<any, void>({
query: () => "/api/v1/permission/"
}),
logout: builder.mutation({
query: (logoutUrl: string) => ({
url: logoutUrl,
method: "POST"
})
}),
login: builder.mutation({
query: (loginUrl: string) => ({
url: loginUrl,
method: "GET"
})
})
})
});

export const { useLazyGetAuthQuery, useLogoutMutation, useLoginMutation } =
authApiSlice;
65 changes: 65 additions & 0 deletions src/features/auth/components/authButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import React, { useState, useEffect } from "react";

import { StyledButtonPrimary } from "../../../styles";
import { PrefContext } from "../../../preferences";
import {
useLazyGetAuthQuery,
useLogoutMutation,
useLoginMutation
} from "../authApiSlice";

export const AuthButton = () => {
const prefs = React.useContext(PrefContext);
const authUrl = prefs.loginUrl;
const pageUrl = window.location.href;
const loginPageUrl = `${authUrl}${pageUrl}`;
const logoutUrl = prefs.logoutUrl;

const [triggerAuthQuery] = useLazyGetAuthQuery();
const [triggerLogout] = useLogoutMutation();
const [triggerLogin] = useLoginMutation();

const [authenticated, setAuthenticated] = useState(false);

const performLogout = async () => {
await triggerLogout(logoutUrl);
setAuthenticated(false);
window.location.href = pageUrl;
};

const performLogin = async () => {
await triggerLogin(loginPageUrl);
setAuthenticated(true);
window.location.href = loginPageUrl;
};

const handleOnClick = () => {
if (authenticated) {
performLogout();
} else {
performLogin();
}
};

const getPermissions = async () => {
const { data: permissions } = await triggerAuthQuery();
setAuthenticated(permissions.data.authenticated);
};

useEffect(() => {
(async () => {
getPermissions();
})();
}, []);

return (
<StyledButtonPrimary
onClick={() => {
handleOnClick();
}}
sx={{ position: "absolute", top: 14, right: 18 }}
>
{authenticated ? "Log out" : "Log in"}
</StyledButtonPrimary>
);
};
1 change: 1 addition & 0 deletions src/features/auth/components/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./authButton";
2 changes: 2 additions & 0 deletions src/features/auth/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./authApiSlice";
export * from "./components";
34 changes: 9 additions & 25 deletions src/features/environments/components/EnvironmentsSearch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@ import Typography from "@mui/material/Typography";
import OutlinedInput from "@mui/material/OutlinedInput";
import Box from "@mui/material/Box";
import InputAdornment from "@mui/material/InputAdornment";
import Link from "@mui/material/Link";
import React from "react";

import { PrefContext } from "../../../preferences";
import { LoginIcon, SearchIconAlt } from "../../../components";
import { getIconForStyleType } from "../../../utils/helpers";
import { SearchIconAlt } from "../../../components";
import { AuthButton } from "../../auth";

interface IEnvironmentsSearchProps {
/**
Expand All @@ -18,27 +17,12 @@ interface IEnvironmentsSearchProps {

export const EnvironmentsSearch = ({ onChange }: IEnvironmentsSearchProps) => {
const prefs = React.useContext(PrefContext);
const showLoginIcon = prefs.showLoginIcon;
const showAuthButton = prefs.showAuthButton;
const isCookieAuthMethod = prefs.authMethod === "cookie";
const authUrl = prefs.loginUrl;
const pageUrl = window.location.href;
const loginPageUrl = `${authUrl}${pageUrl}`;
let login;
let authButton;

const loginIcon = getIconForStyleType(
<LoginIcon color="#686868" />,
<LoginIcon color="#B9D9BD" />
);

if (showLoginIcon && isCookieAuthMethod) {
login = (
<Link
href={loginPageUrl}
sx={{ position: "absolute", top: 14, right: 18 }}
>
{loginIcon}
</Link>
);
if (showAuthButton && isCookieAuthMethod) {
authButton = <AuthButton></AuthButton>;
}

return (
Expand All @@ -52,16 +36,16 @@ export const EnvironmentsSearch = ({ onChange }: IEnvironmentsSearchProps) => {
data-testid="env-search-title"
sx={{
marginBottom: "14px",
textAlign: "center",
textAlign: "left",
color: " #333",
fontWeight: 600,
fontSize: "14px",
marginTop: "30px"
marginTop: "40px"
}}
>
Package Manager
</Typography>
{login}
{authButton}
<OutlinedInput
onChange={onChange}
size="small"
Expand Down
32 changes: 22 additions & 10 deletions src/preferences.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ export interface IPreferences {
authToken: string;
loginUrl: string;
styleType: string;
showLoginIcon: boolean;
showAuthButton: boolean;
logoutUrl: string;
}

const { condaStoreConfig = {} } =
Expand Down Expand Up @@ -38,12 +39,17 @@ export const prefDefault: Readonly<IPreferences> = {
condaStoreConfig.REACT_APP_STYLE_TYPE ??
"grayscale",

showLoginIcon: process.env.REACT_APP_SHOW_LOGIN_ICON
? JSON.parse(process.env.REACT_APP_SHOW_LOGIN_ICON)
showAuthButton: process.env.REACT_APP_SHOW_AUTH_BUTTON
? JSON.parse(process.env.REACT_APP_SHOW_AUTH_BUTTON)
: condaStoreConfig !== undefined &&
condaStoreConfig.REACT_APP_SHOW_LOGIN_ICON !== undefined
? JSON.parse(condaStoreConfig.REACT_APP_SHOW_LOGIN_ICON)
: true
condaStoreConfig.REACT_APP_SHOW_AUTH_BUTTON !== undefined
? JSON.parse(condaStoreConfig.REACT_APP_SHOW_AUTH_BUTTON)
: true,

logoutUrl:
process.env.REACT_APP_LOGOUT_PAGE_URL ??
condaStoreConfig.REACT_APP_LOGOUT_PAGE_URL ??
"http://localhost:5000/conda-store/logout?next=/"
};

export class Preferences implements IPreferences {
Expand Down Expand Up @@ -71,8 +77,12 @@ export class Preferences implements IPreferences {
return this._styleType;
}

get showLoginIcon() {
return this._showLoginIcon;
get showAuthButton() {
return this._showAuthButton;
}

get logoutUrl() {
return this._logoutUrl;
}

set(pref: IPreferences) {
Expand All @@ -81,15 +91,17 @@ export class Preferences implements IPreferences {
this._authToken = pref.authToken;
this._loginUrl = pref.loginUrl;
this._styleType = pref.styleType;
this._showLoginIcon = pref.showLoginIcon;
this._showAuthButton = pref.showAuthButton;
this._logoutUrl = pref.logoutUrl;
}

private _apiUrl: IPreferences["apiUrl"];
private _authMethod: IPreferences["authMethod"];
private _authToken: IPreferences["authToken"];
private _loginUrl: IPreferences["loginUrl"];
private _styleType: IPreferences["styleType"];
private _showLoginIcon: IPreferences["showLoginIcon"];
private _showAuthButton: IPreferences["showAuthButton"];
private _logoutUrl: IPreferences["logoutUrl"];
}

export const prefGlobal = new Preferences();
Expand Down

0 comments on commit aae1a55

Please sign in to comment.