Skip to content
Snippets Groups Projects
Commit fa580d9a authored by Hamza Remmal's avatar Hamza Remmal :homes:
Browse files

Add the EPFL themed login form

parent 79311031
No related branches found
No related tags found
1 merge request!309Add the EPFL themed login form
Showing
with 127 additions and 6 deletions
...@@ -13,7 +13,6 @@ import org.springframework.core.Ordered; ...@@ -13,7 +13,6 @@ import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order; import org.springframework.core.annotation.Order;
import org.springframework.ldap.core.support.BaseLdapPathContextSource; import org.springframework.ldap.core.support.BaseLdapPathContextSource;
import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
...@@ -47,10 +46,10 @@ public class SecurityConfig { ...@@ -47,10 +46,10 @@ public class SecurityConfig {
@Bean @Bean
@Order(1) @Order(1)
public SecurityFilterChain filterChain(HttpSecurity http, ShareSecretAuthenticationProvider provider) throws Exception { public SecurityFilterChain filterChain(HttpSecurity http, ShareSecretAuthenticationProvider provider) throws Exception {
http.apply(new SharedSecretConfigurer<>());
return http return http
.securityMatcher("/api/**") .securityMatcher("/api/**")
.authenticationManager(new ProviderManager(provider)) .with(new SharedSecretConfigurer<>(), withDefaults())
.authenticationProvider(provider)
.csrf(AbstractHttpConfigurer::disable) .csrf(AbstractHttpConfigurer::disable)
.anonymous(AbstractHttpConfigurer::disable) .anonymous(AbstractHttpConfigurer::disable)
.httpBasic(AbstractHttpConfigurer::disable) .httpBasic(AbstractHttpConfigurer::disable)
...@@ -94,18 +93,23 @@ public class SecurityConfig { ...@@ -94,18 +93,23 @@ public class SecurityConfig {
// Match on anything but the api endpoints (these are authentication via the shared secret scheme only) // Match on anything but the api endpoints (these are authentication via the shared secret scheme only)
.securityMatcher(new NegatedRequestMatcher(new MvcRequestMatcher(introspector, "/api/**"))) .securityMatcher(new NegatedRequestMatcher(new MvcRequestMatcher(introspector, "/api/**")))
.authenticationManager(manager) .authenticationManager(manager)
.httpBasic(withDefaults()) .formLogin(conf -> {
conf.loginPage("/login");
conf.usernameParameter("username");
conf.passwordParameter("password");
conf.permitAll();
})
.csrf(AbstractHttpConfigurer::disable) .csrf(AbstractHttpConfigurer::disable)
.anonymous(AbstractHttpConfigurer::disable) .anonymous(AbstractHttpConfigurer::disable)
.requestCache(RequestCacheConfigurer::disable) .requestCache(RequestCacheConfigurer::disable)
.rememberMe(AbstractHttpConfigurer::disable) .rememberMe(AbstractHttpConfigurer::disable)
// Disable session management until we figure a way for replicated and distributed services // Disable session management until we figure a way for replicated and distributed services
.sessionManagement(AbstractHttpConfigurer::disable) .sessionManagement(AbstractHttpConfigurer::disable)
.formLogin(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(auth -> { .authorizeHttpRequests(auth -> {
auth.requestMatchers("/error", "/css/error-pages.css", "/images/favicons/**").permitAll(); auth.requestMatchers("/error", "/css/error-pages.css", "/images/favicons/**", "/images/logos/**", "/images/pictograms/**").permitAll();
auth.anyRequest().hasAuthority(AutogradeAuthorities.SYSTEM_ACCESS.getAuthority()); auth.anyRequest().hasAuthority(AutogradeAuthorities.SYSTEM_ACCESS.getAuthority());
}) })
.logout(withDefaults())
.build(); .build();
} }
......
package ch.epfl.autograde.controller; package ch.epfl.autograde.controller;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller @Controller
public class IndexController { public class IndexController {
@GetMapping("/login")
private String login() {
return "login";
}
} }
autograde-service/src/main/resources/static/images/logos/epfl-logo-black.png

15.2 KiB

autograde-service/src/main/resources/static/images/logos/epfl-logo-red.png

16.8 KiB

autograde-service/src/main/resources/static/images/logos/epfl-logo-white.png

15.6 KiB

<svg class="h-5 w-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
<circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="2" fill="none" />
<path d="M15 9L9 15M9 9l6 6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
</svg>
\ No newline at end of file
<svg class="h-5 w-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd" />
</svg>
\ No newline at end of file
<div aria-live="assertive" class="fixed inset-0 flex items-start px-4 py-6 pointer-events-none sm:p-6 sm:items-start">
<div class="w-full flex flex-col items-center space-y-4 sm:items-end">
<div id="notification" class="max-w-sm w-full bg-white shadow-lg rounded-lg pointer-events-auto ring-1 ring-black ring-opacity-5 overflow-hidden transition-opacity duration-500 ease-in-out opacity-100">
<div class="p-4">
<div class="flex items-center justify-between">
<div class="flex items-center">
<div class="flex-shrink-0">
<!-- TODO: Use the pictograms from /images/pictograms/ -->
<svg class="h-5 w-5 text-red-600" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
<circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="2" fill="none" />
<path d="M15 9L9 15M9 9l6 6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
</svg>
</div>
<p class="ml-3 text-sm font-medium text-gray-900">Error</p>
</div>
<button type="button" onclick="closeAlert()" class="bg-white rounded-md inline-flex text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500">
<span class="sr-only">Close</span>
<!-- TODO: Use the pictograms from /images/pictograms/ -->
<svg class="h-5 w-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd" />
</svg>
</button>
</div>
<p class="mt-1 text-sm text-gray-500">Invalid username or password.</p>
</div>
</div>
</div>
</div>
<script>
function closeAlert() {
const notification = document.getElementById('notification');
notification.classList.replace('opacity-100', 'opacity-0');
setTimeout(() => notification.style.display = 'none', 500);
}
</script>
<!DOCTYPE html>
<html xmlns:th="https://www.thymeleaf.org" lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Login - AUTOGRADE</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="shortcut icon" th:href="@{/images/favicons/favicon.ico}">
<link rel="apple-touch-icon-precomposed" th:href="@{/images/favicons/favicon-152.png}">
<link rel="icon" th:href="@{/images/favicons/favicon-16.png}" sizes="16x16">
<link rel="icon" th:href="@{/images/favicons/favicon-32.png}" sizes="32x32">
<link rel="icon" th:href="@{/images/favicons/favicon-57.png}" sizes="57x57">
<link rel="icon" th:href="@{/images/favicons/favicon-76.png}" sizes="76x76">
<link rel="icon" th:href="@{/images/favicons/favicon-96.png}" sizes="96x96">
<link rel="icon" th:href="@{/images/favicons/favicon-120.png}" sizes="120x120">
<link rel="icon" th:href="@{/images/favicons/favicon-128.png}" sizes="128x128">
<link rel="icon" th:href="@{/images/favicons/favicon-152.png}" sizes="152x152">
<link rel="icon" th:href="@{/images/favicons/favicon-180.png}" sizes="180x180">
<link rel="shortcut icon" th:href="@{/images/favicons/android-chrome-192x192.png}" sizes="192x192">
<link rel="shortcut icon" th:href="@{/images/favicons/android-chrome-512x512.png}" sizes="512x512">
<link rel="icon" th:href="@{/images/favicons/favicon-228.png}" sizes="228x228">
<link rel="apple-touch-icon" th:href="@{/images/favicons/apple-touch-icon.png}">
<link rel="apple-touch-icon" th:href="@{/images/favicons/favicon-152.png}" sizes="152x152">
<link rel="apple-touch-icon" th:href="@{/images/favicons/favicon-180.png}" sizes="180x180">
</head>
<body class="h-screen bg-gray-50">
<th:block th:if="${param.error}">
<div th:replace="fragments/error-notification"></div>
</th:block>
<div class="flex h-full items-center justify-center px-6 py-12 lg:px-8">
<div class="max-w-lg mx-auto bg-white shadow-lg rounded-lg p-6">
<div class="sm:mx-auto sm:w-full sm:max-w-sm">
<img class="mx-auto w-auto" th:src="@{/images/logos/epfl-logo-red.png}" alt="EPFL Logo">
<h2 class="text-center text-2xl/9 font-bold tracking-tight text-gray-900">Sign in to Autograde</h2>
</div>
<hr class="my-6 border-gray-200">
<div class="mt-10 sm:mx-auto sm:w-full sm:max-w-sm">
<form class="space-y-6" th:action="@{/login}" method="POST">
<div>
<label for="username" class="block text-sm/6 font-medium text-gray-900">Username</label>
<div class="mt-2">
<input type="text" name="username" id="username" autocomplete="username" required class="block w-full rounded-md bg-white px-3 py-1.5 text-base text-gray-900 outline outline-1 -outline-offset-1 outline-gray-300 placeholder:text-gray-400 focus:outline focus:outline-2 focus:-outline-offset-2 focus:outline-red-600 sm:text-sm/6">
</div>
</div>
<div>
<div class="flex items-center justify-between">
<label for="password" class="block text-sm/6 font-medium text-gray-900">Password</label>
<div class="text-sm">
<a th:href="@{/forgot-password}" class="font-semibold text-red-600 hover:text-red-500">Forgot password?</a>
</div>
</div>
<div class="mt-2">
<input type="password" name="password" id="password" autocomplete="current-password" required class="block w-full rounded-md bg-white px-3 py-1.5 text-base text-gray-900 outline outline-1 -outline-offset-1 outline-gray-300 placeholder:text-gray-400 focus:outline focus:outline-2 focus:-outline-offset-2 focus:outline-red-600 sm:text-sm/6">
</div>
</div>
<div>
<button type="submit" class="flex w-full justify-center rounded-md bg-red-600 px-3 py-1.5 text-sm/6 font-semibold text-white shadow-sm hover:bg-red-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-red-600">Sign in</button>
</div>
</form>
</div>
</div>
</div>
</body>
</html>
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment