Skip to content

Commit

Permalink
Add ALA skinned UI #1
Browse files Browse the repository at this point in the history
 - Add Thymeleaf dialect with tags to get and process banner and footer
 - Add additional properties for skin settings
 - Add additional web flow support for adding new properties to flow scope
 - Add ala theme properties
 - Add i18n messages for ALA login screen
 - Add image for ALA login screen
 - Set default theme to ALA
  • Loading branch information
sbearcsiro committed Aug 16, 2017
1 parent 9b98720 commit e7d16df
Show file tree
Hide file tree
Showing 16 changed files with 462 additions and 27 deletions.
82 changes: 79 additions & 3 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<artifactId>cas</artifactId>
<packaging>war</packaging>
<!-- Version should ${cas.version}-${ala.build.version}(-SNAPSHOT)? -->
<version>5.1.2-1-SNAPSHOT</version>
<version>5.1.3-1-SNAPSHOT</version>

<build>
<plugins>
Expand Down Expand Up @@ -197,6 +197,12 @@
<groupId>org.apereo.cas</groupId>
<artifactId>cas-server-support-jdbc</artifactId>
<version>${cas.version}</version>
<exclusions>
<!--<exclusion>-->
<!--<groupId>com.zaxxer</groupId>-->
<!--<artifactId>HikariCP-java6</artifactId>-->
<!--</exclusion>-->
</exclusions>
</dependency>
<dependency>
<groupId>org.apereo.cas</groupId>
Expand Down Expand Up @@ -384,6 +390,13 @@
<artifactId>cas-server-webapp-init</artifactId>
<version>${cas.version}</version>
</dependency>
<!-- Theoretically unnecessary... -->
<dependency>
<groupId>com.fasterxml</groupId>
<artifactId>classmate</artifactId>
<version>1.3.3</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
Expand All @@ -401,14 +414,77 @@
<artifactId>simple-jndi</artifactId>
<version>0.15.0</version>
</dependency>
<!-- need to move this into compile time -->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring4</artifactId>
<version>3.0.7.RELEASE</version>
<scope>compile</scope>
</dependency>
<!-- Will be included in CAS 5.2 -->
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>2.5.4</version>
<scope>compile</scope>
</dependency>
<!-- Here be webjars -->
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>1.12.4</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery-ui</artifactId>
<version>1.12.1</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery-cookie</artifactId>
<version>1.4.1-1</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery-migrate</artifactId>
<version>1.4.1</version>
</dependency>
<!--<dependency>-->
<!--<groupId>org.webjars</groupId>-->
<!--<artifactId>jQuery-Autocomplete</artifactId>-->
<!--<version>1.2.7</version>-->
<!--</dependency>-->
<dependency>
<groupId>org.webjars</groupId>
<artifactId>headjs</artifactId>
<version>1.0.3</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>font-awesome</artifactId>
<version>4.7.0</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>zxcvbn</artifactId>
<version>4.3.0</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>bootstrap-social</artifactId>
<version>5.0.0</version>
</dependency>



</dependencies>

<properties>
<cas.version>5.1.2</cas.version>
<cas.version>5.1.3</cas.version>
<springboot.version>1.5.3.RELEASE</springboot.version>
<!-- app.server could be -jetty, -undertow, -tomcat, or blank if you plan to provide appserver -->
<app.server>-tomcat</app.server>
<kotlin.version>1.1.3-2</kotlin.version>
<kotlin.version>1.1.4</kotlin.version>
<kotlin.compiler.incremental>true</kotlin.compiler.incremental>
<kotlin.compiler.jvmTarget>1.8</kotlin.compiler.jvmTarget>
<maven.compiler.source>1.8</maven.compiler.source>
Expand Down
16 changes: 15 additions & 1 deletion src/main/kotlin/au/org/ala/cas/AlaCasProperties.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import org.springframework.boot.context.properties.ConfigurationProperties
@ConfigurationProperties(value = "ala")
open class AlaCasProperties(
var cookie: CookieProperties = CookieProperties(),
var userCreator: UserCreatorProperties = UserCreatorProperties()
var userCreator: UserCreatorProperties = UserCreatorProperties(),
val skin: SkinProperties = SkinProperties()
)

open class UserCreatorProperties(
Expand All @@ -22,3 +23,16 @@ open class UserCreatorProperties(

}

open class SkinProperties {
lateinit var baseUrl: String
lateinit var headerFooterUrl: String
lateinit var favIconBaseUrl: String
lateinit var bieBaseUrl: String
lateinit var bieSearchPath: String
lateinit var userDetailsUrl: String
lateinit var orgShortName: String
lateinit var orgLongName: String
lateinit var orgNameKey: String
var cacheDuration: Long = 1800_000
}

Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package au.org.ala.cas.delegated;

import au.org.ala.cas.delegated.UserCreator
import au.org.ala.cas.delegated.UserCreatorALA
import au.org.ala.cas.AlaCasProperties
import au.org.ala.utils.logger
import org.apereo.cas.authentication.principal.PrincipalFactory
Expand Down
18 changes: 18 additions & 0 deletions src/main/kotlin/au/org/ala/cas/thymeleaf/AlaDialect.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package au.org.ala.cas.thymeleaf

import org.thymeleaf.dialect.AbstractProcessorDialect
import org.thymeleaf.processor.IProcessor

class AlaDialect(val alaTemplateClient: AlaTemplateClient) : AbstractProcessorDialect("ALA Dialect", "ala", 10) {

override fun getProcessors(dialectPrefix: String): MutableSet<IProcessor> {
return mutableSetOf(
AlaHeaderFooterTagProcessor(dialectPrefix, "banner", alaTemplateClient),
AlaHeaderFooterTagProcessor(dialectPrefix, "footer", alaTemplateClient)
)
}


//override fun getExpressionObjectFactory() = AlaExpressionFactory(skinConfig)
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package au.org.ala.cas.thymeleaf

import org.thymeleaf.context.ITemplateContext
import org.thymeleaf.context.WebEngineContext
import org.thymeleaf.model.IProcessableElementTag
import org.thymeleaf.processor.element.AbstractElementTagProcessor
import org.thymeleaf.processor.element.IElementTagStructureHandler
import org.thymeleaf.templatemode.TemplateMode

class AlaHeaderFooterTagProcessor(
dialectPrefix: String,
val tagName: String,
val alaTemplateClient: AlaTemplateClient) :
AbstractElementTagProcessor(
TemplateMode.HTML,
dialectPrefix,
tagName, // the tag name to match
true, // apply dialect prefix to tag name
null, // attribute name
false, // apply dialect prefix to attribute name
PRECEDENCE // precedence inside dialect's precedence
) {

companion object {
const val PRECEDENCE = 1000
}

override fun doProcess(context: ITemplateContext, tag: IProcessableElementTag, structureHandler: IElementTagStructureHandler) {
val request = when (context) {
is WebEngineContext -> context.request
else -> null
}
val content = alaTemplateClient.load(tagName, request)
if (content == null) {
structureHandler.replaceWith("<div th:replace=\"fragments/$tagName\"></div>", true)
} else {
structureHandler.replaceWith(content, false)
}
}


}
87 changes: 87 additions & 0 deletions src/main/kotlin/au/org/ala/cas/thymeleaf/AlaTemplateClient.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package au.org.ala.cas.thymeleaf

import au.org.ala.cas.SkinProperties
import au.org.ala.utils.logger
import com.github.benmanes.caffeine.cache.Caffeine
import org.apereo.cas.web.support.WebUtils
import org.springframework.webflow.execution.RequestContextHolder
import java.io.Reader
import java.net.URI
import java.util.concurrent.TimeUnit
import javax.servlet.http.HttpServletRequest

class AlaTemplateClient(val skinConfig: SkinProperties, val cookieName: String) {

companion object {
const val LOGGED_IN_CLASS = "logged-in"
const val LOGGED_OUT_CLASS = "not-logged-in"

val log = logger<AlaHeaderFooterTagProcessor>()
}

val uri = URI(skinConfig.headerFooterUrl)
val cache = Caffeine.newBuilder().expireAfterWrite(skinConfig.cacheDuration, TimeUnit.MILLISECONDS).build(this::loadTemplate)

fun loadTemplate(template: String) = uri.resolve("./$template.html").toURL().openStream().reader().use(Reader::readText)

fun load(name: String, request: HttpServletRequest?, fluidLayout: Boolean = false): String? {
val cached = try {
cache[name]
} catch (e: Exception) {
log.error("Couldn't load {}", name, e)
null
}

if (cached == null || cached.isBlank()) {
return null
}
var content = cached.replace("::headerFooterServer::", skinConfig.headerFooterUrl)
.replace("::centralServer::", skinConfig.baseUrl)
.replace("::searchServer::", skinConfig.bieBaseUrl)
.replace("::searchPath::", skinConfig.bieSearchPath)
.replace("::authStatusClass::", if (isLoggedIn(request)) LOGGED_IN_CLASS else LOGGED_OUT_CLASS)
if (fluidLayout) {
content = content.replace("class=\"container\"", "class=\"container-fluid\"")
}
if (content.contains("::loginLogoutListItem::")) {
// only do the work if it is needed
content = content.replace("::loginLogoutListItem::", buildLoginoutLink(request))
}
return content
}

fun isLoggedIn(request: HttpServletRequest?): Boolean {
val ctx = RequestContextHolder.getRequestContext()
return if (ctx != null) {
// TODO pass in the request context
WebUtils.getCredential(ctx) != null
// (request.cookies?.any { it.name == cookieName } ?: false) || request.userPrincipal != null
} else { false }
}

/**
* Builds the login or logout link based on current login status.
* @param attrs any specified params to override defaults
* @return
*/
fun buildLoginoutLink(request: HttpServletRequest?): String {
// val requestUri = removeContext(grailServerURL) + request.forwardURI
// val logoutUrl = attrs.logoutUrl ?: grailServerURL + "/session/logout"
// val logoutReturnToUrl = attrs.logoutReturnToUrl ?: requestUri
// def casLogoutUrl = attrs.casLogoutUrl ?: casLogoutUrl
//
// // TODO should this be attrs.logoutReturnToUrl?
// if (!attrs.loginReturnToUrl && request.queryString) {
// logoutReturnToUrl += "?" + URLEncoder.encode(request.queryString, "UTF-8")
// }

return if (isLoggedIn(request)) {
val casLogoutUrl = request?.servletContext?.contextPath + "/logout"
"<a href=\"$casLogoutUrl\">Logout</a>"
} else {
// currently logged out
val casLoginUrl = request?.servletContext?.contextPath + "/login"
"<a href=\"$casLoginUrl\">Log in</a>"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package au.org.ala.cas.thymeleaf

import au.org.ala.cas.AlaCasProperties
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

@Configuration
@EnableConfigurationProperties(AlaCasProperties::class)
open class AlaThymeleafConfiguration {

@Autowired
lateinit var alaCasProperties: AlaCasProperties

@Bean
open fun alaTemplateClient() = AlaTemplateClient(alaCasProperties.skin, alaCasProperties.cookie.name)

@Bean
open fun alaDialect(alaTemplateClient: AlaTemplateClient) = AlaDialect(alaTemplateClient)

// @Autowired
// lateinit var templateEngine: SpringTemplateEngine
//
// @PostConstruct
// fun extension() {
// templateEngine.addDialect(alaDialect(alaTemplateClient()))
// val resolver = UrlTemplateResolver()
// resolver.prefix = alaCasProperties.skin.headerFooterUrl ?: "https://www.ala.org.au/commonui"
// resolver.suffix = ".html"
// resolver.setTemplateMode("HTML5")
// resolver.order = templateEngine.templateResolvers.size
// resolver.isCacheable = true
// templateEngine.addTemplateResolver(resolver)
// }
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package au.org.ala.cas.webflow

import au.org.ala.cas.AlaCasProperties
import org.apereo.cas.services.ServicesManager
import org.apereo.cas.ticket.registry.TicketRegistrySupport
import org.apereo.cas.web.support.CookieRetrievingCookieGenerator
import org.springframework.beans.factory.annotation.Autowired
Expand All @@ -12,6 +13,8 @@ import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.webflow.definition.registry.FlowDefinitionRegistry
import org.springframework.webflow.engine.builder.support.FlowBuilderServices
import org.springframework.webflow.execution.Action


@Configuration("alaCasWebflowConfiguration")
@EnableConfigurationProperties(AlaCasProperties::class)
Expand Down Expand Up @@ -55,11 +58,20 @@ open class AlaCasWebflowConfiguration {
): RemoveAuthCookieAction =
RemoveAuthCookieAction(alaProxyAuthenticationCookieGenerator)

@Bean
@RefreshScope
open fun initializeLoginAction(): AlaInitializeLoginAction {
return AlaInitializeLoginAction(alaCasProperties)
}

@ConditionalOnMissingBean(name = arrayOf("authCookieWebflowConfigurer"))
@Bean("authCookieWebflowConfigurer")
open fun authCookieWebflowConfigurer(
generateAuthCookieAction: GenerateAuthCookieAction,
removeAuthCookieAction: RemoveAuthCookieAction
): AuthCookieWebflowConfigurer =
AuthCookieWebflowConfigurer(flowBuilderServices, loginFlowDefinitionRegistry, logoutFlowDefinitionRegistry, generateAuthCookieAction, removeAuthCookieAction)
}
removeAuthCookieAction: RemoveAuthCookieAction,
initializeLoginAction: AlaInitializeLoginAction
): AlaCasWebflowConfigurer =
AlaCasWebflowConfigurer(flowBuilderServices, loginFlowDefinitionRegistry, logoutFlowDefinitionRegistry, generateAuthCookieAction, removeAuthCookieAction, initializeLoginAction)

}

Loading

0 comments on commit e7d16df

Please sign in to comment.