Skip to content

Commit

Permalink
Merge pull request #411 from cferbar/master
Browse files Browse the repository at this point in the history
Issue #353: Enable anonymous access
  • Loading branch information
dularion authored Aug 18, 2017
2 parents 275d0aa + 2866063 commit 44a7377
Show file tree
Hide file tree
Showing 8 changed files with 309 additions and 8 deletions.
6 changes: 5 additions & 1 deletion grails-app/assets/javascripts/streama/streama.run.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
angular.module('streama').run(function ($rootScope, $state, localStorageService, apiService, modalService, userService) {
angular.module('streama').run(function ($window, $rootScope, $state, localStorageService, apiService, modalService, userService) {
apiService.currentUser().success(function (data) {
userService.setCurrentUser(data);
});
Expand Down Expand Up @@ -42,6 +42,10 @@ angular.module('streama').run(function ($rootScope, $state, localStorageService,
$rootScope.$broadcast('changedGenre', genre);
};

$rootScope.loginUser = function () {
$window.location.assign('/login/login');
}


$rootScope.$on('$stateChangeSuccess', function (e, toState) {
$rootScope.toggleGenreMenu(true);
Expand Down
168 changes: 168 additions & 0 deletions grails-app/controllers/streama/LoginController.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
package streama


import grails.converters.JSON
import grails.plugin.springsecurity.SpringSecurityUtils
import org.springframework.security.access.annotation.Secured
import org.springframework.security.authentication.AccountExpiredException
import org.springframework.security.authentication.AuthenticationTrustResolver
import org.springframework.security.authentication.CredentialsExpiredException
import org.springframework.security.authentication.DisabledException
import org.springframework.security.authentication.LockedException
import org.springframework.security.core.Authentication
import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.security.web.WebAttributes

import javax.servlet.http.HttpServletResponse
import javax.servlet.http.HttpSession

@Secured('permitAll')
class LoginController {

/** Dependency injection for the authenticationTrustResolver. */
AuthenticationTrustResolver authenticationTrustResolver

/** Dependency injection for the springSecurityService. */
def springSecurityService

/** Dependency injection for the settingsService. */
def settingsService

def login() {
SecurityContextHolder.clearContext();

HttpSession session = request.getSession(false);
if (session != null) {
session.invalidate();
}

def conf = getConf()

if (springSecurityService.isLoggedIn()) {
redirect uri: conf.successHandler.defaultTargetUrl
return
}

String postUrl = request.contextPath + conf.apf.filterProcessesUrl
render view: 'auth', model: [postUrl: postUrl,
rememberMeParameter: conf.rememberMe.parameter,
usernameParameter: conf.apf.usernameParameter,
passwordParameter: conf.apf.passwordParameter,
gspLayout: conf.gsp.layoutAuth]

}

/** Default action; redirects to 'defaultTargetUrl' if logged in, /login/auth otherwise. */
def index() {

if (springSecurityService.isLoggedIn()) {
redirect uri: conf.successHandler.defaultTargetUrl
}
else {
redirect action: 'auth', params: params
}
}

/** Show the login page. */
def auth() {

/** Check if anonymous access is enabled, to avoid login **/
if (settingsService.anonymousAccess) {
User anonymous = User.findByUsername("anonymous")
springSecurityService.reauthenticate(anonymous.username,anonymous.password)
}

def conf = getConf()

if (springSecurityService.isLoggedIn()) {
redirect uri: conf.successHandler.defaultTargetUrl
return
}

String postUrl = request.contextPath + conf.apf.filterProcessesUrl
render view: 'auth', model: [postUrl: postUrl,
rememberMeParameter: conf.rememberMe.parameter,
usernameParameter: conf.apf.usernameParameter,
passwordParameter: conf.apf.passwordParameter,
gspLayout: conf.gsp.layoutAuth]
}

/** The redirect action for Ajax requests. */
def authAjax() {
response.setHeader 'Location', conf.auth.ajaxLoginFormUrl
render(status: HttpServletResponse.SC_UNAUTHORIZED, text: 'Unauthorized')
}

/** Show denied page. */
def denied() {
if (springSecurityService.isLoggedIn() && authenticationTrustResolver.isRememberMe(authentication)) {
// have cookie but the page is guarded with IS_AUTHENTICATED_FULLY (or the equivalent expression)
redirect action: 'full', params: params
return
}

[gspLayout: conf.gsp.layoutDenied]
}

/** Login page for users with a remember-me cookie but accessing a IS_AUTHENTICATED_FULLY page. */
def full() {
def conf = getConf()
render view: 'auth', params: params,
model: [hasCookie: authenticationTrustResolver.isRememberMe(authentication),
postUrl: request.contextPath + conf.apf.filterProcessesUrl,
rememberMeParameter: conf.rememberMe.parameter,
usernameParameter: conf.apf.usernameParameter,
passwordParameter: conf.apf.passwordParameter,
gspLayout: conf.gsp.layoutAuth]
}

/** Callback after a failed login. Redirects to the auth page with a warning message. */
def authfail() {

String msg = ''
def exception = session[WebAttributes.AUTHENTICATION_EXCEPTION]
if (exception) {
if (exception instanceof AccountExpiredException) {
msg = message(code: 'springSecurity.errors.login.expired')
}
else if (exception instanceof CredentialsExpiredException) {
msg = message(code: 'springSecurity.errors.login.passwordExpired')
}
else if (exception instanceof DisabledException) {
msg = message(code: 'springSecurity.errors.login.disabled')
}
else if (exception instanceof LockedException) {
msg = message(code: 'springSecurity.errors.login.locked')
}
else {
msg = message(code: 'springSecurity.errors.login.fail')
}
}

if (springSecurityService.isAjax(request)) {
render([error: msg] as JSON)
}
else {
flash.message = msg
redirect action: 'auth', params: params
}
}

/** The Ajax success redirect url. */
def ajaxSuccess() {
render([success: true, username: authentication.name] as JSON)
}

/** The Ajax denied redirect url. */
def ajaxDenied() {
render([error: 'access denied'] as JSON)
}

protected Authentication getAuthentication() {
SecurityContextHolder.context?.authentication
}

protected ConfigObject getConf() {
SpringSecurityUtils.securityConfig
}
}
8 changes: 7 additions & 1 deletion grails-app/controllers/streama/SettingsController.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,17 @@ class SettingsController {
@Transactional
def updateMultiple() {
def settings = request.JSON

settings.each{ settingData ->
Settings settingsInstance = Settings.get(settingData?.id)
settingsInstance.properties = settingData
settingsInstance.save failOnError: true
if (settingsInstance.name == 'anonymous_access') {
if (Boolean.valueOf(settingData.value)) {
settingsService.enableAnonymousUser()
} else {
settingsService.disableAnonymousUser()
}
}
}

respond settings
Expand Down
24 changes: 19 additions & 5 deletions grails-app/controllers/streama/UserController.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import grails.transaction.Transactional
class UserController {

def validationService
def settingsService
def mailService
def springSecurityService
def passwordEncoder
Expand Down Expand Up @@ -50,11 +51,17 @@ class UserController {
return
}

userInstance.deleted = true
userInstance.username = userInstance.username + (randomUUID() as String)
userInstance.accountExpired = true
/** the anonymous user is different, and disabled the anonymous_access property **/
if (userInstance.username == "anonymous") {
settingsService.disableAnonymousUser()
settingsService.changeAnonymousAccess("false")
} else {
userInstance.deleted = true
userInstance.username = userInstance.username + (randomUUID() as String)
userInstance.accountExpired = true

userInstance.save flush: true, failOnError: true
userInstance.save flush: true, failOnError: true
}

render status: NO_CONTENT
}
Expand Down Expand Up @@ -92,7 +99,7 @@ class UserController {
}


if (!userInstance.invitationSent && userInstance.enabled && userInstance.username != "admin") {
if (!userInstance.invitationSent && userInstance.enabled && userInstance.username != "admin" && userInstance.username != "anonymous") {
userInstance.uuid = randomUUID() as String

try {
Expand All @@ -109,6 +116,13 @@ class UserController {
userInstance.invitationSent = true
}

if (userInstance.isDirty('username') && userInstance.getPersistentValue('username') == "anonymous") {
settingsService.changeAnonymousAccess("false")
}

if (userInstance.username == "anonymous") {
settingsService.changeAnonymousAccess(userInstance.enabled.toString())
}

userInstance.save flush: true

Expand Down
7 changes: 7 additions & 0 deletions grails-app/services/streama/DefaultDataService.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,13 @@ class DefaultDataService {
description: 'Should the First-Time login info (admin/admin) be shown in the login screen?',
settingsType: 'boolean'
],
[
settingsKey: 'Allow anonymous access',
name: 'anonymous_access',
description: 'Allow to reproduce videos without login in the application',
settingsType: 'boolean',
value: 'false'
],
[
settingsKey: 'Show Version Number',
name: 'show_version_num',
Expand Down
29 changes: 29 additions & 0 deletions grails-app/services/streama/SettingsService.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,35 @@ class SettingsService {
return Settings.findBySettingsKey('Base URL')?.value
}

Boolean getAnonymousAccess() {
return Boolean.valueOf(Settings.findByName('anonymous_access')?.value)
}

def enableAnonymousUser() {
User anonymous = User.findByUsername("anonymous")
if (anonymous) {
anonymous.enabled = true
anonymous.deleted = false /** If user has been previously mark as deleted, clear the field **/
} else { /** If the user not exists, or has been deleted, create it **/
anonymous = new User(username: 'anonymous', password: 'anonymous', fullName: 'Anonymous', enabled: true)
}
anonymous.save failOnError: true
}

def changeAnonymousAccess(String value) {
Settings setting = Settings.findByName("anonymous_access" )
setting.value = value
setting.save failOnError: true
}

def disableAnonymousUser() {
/** Delete the user of the database */
User anonymous = User.findByUsername("anonymous")
if (anonymous) {
anonymous.delete failOnError: true
}
}

def validate(Settings settingsInstance) {
def resultValue = [:]

Expand Down
7 changes: 6 additions & 1 deletion grails-app/views/index.gsp
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,12 @@
</head>

<body class="ng-cloak">
<g:render template="/templates/header"></g:render>
<g:if test="${sec.username() == 'anonymous'}">
<g:render template="/templates/header_anonymous"></g:render>
</g:if>
<g:else>
<g:render template="/templates/header"></g:render>
</g:else>

<div class="content ng-cloak">
<ui-view/>
Expand Down
68 changes: 68 additions & 0 deletions grails-app/views/templates/_header_anonymous.gsp
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<header class="main" ng-if="!isCurrentState('player')">
<div class="pull-left flex">
<a class="logo" ui-sref="dash">
<img ng-show="$root.getSetting('logo')" ng-src="{{$root.getSetting('logo').value}}" src="/assets/logo.png" alt="${streama.Settings.findByName('title').value}">
<div ng-show="$root.getSetting('show_version_num').value == true" class="version">v${grailsApplication.metadata.getApplicationVersion()}</div>
<div class="spinner" ng-show="baseData.loading">
<div class="bounce1"></div>
<div class="bounce2"></div>
<div class="bounce3"></div>
</div>
</a>

<div class="browse-genres" ng-if="isCurrentState('dash') && genres.length">
<button class="btn btn-link toggle-menu" ng-click="toggleGenreMenu()">
<span ng-if="selectedGenre" ng-bind="selectedGenre.name"></span>
<span ng-if="!selectedGenre">{{'DASHBOARD.BROWSE_GENRES' | translate}}</span>
<i class="ion-android-arrow-dropdown"></i>
</button>

<div class="toggle-menu-content" ng-show="genreMenuOpen">
<i class="ion-close-circled pull-right" ng-click="toggleGenreMenu()"></i>
<ul>
<li>
<a ng-click="changeGenre()"><i class="ion-grid"></i> All</a>
</li>
<li ng-repeat="genre in ::genres">
<a ng-click="changeGenre(genre)" ng-bind="::genre.name"></a>
</li>
</ul>
</div>
</div>
</div>

<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">

<li ng-if="isCurrentState('dash')">
<div class="dash-search form-group has-feedback">
<input type="text" placeholder="Search.." class="form-control input-xs" ng-model="dashSearch"
typeahead-append-to-body="true" uib-typeahead="(item.title || item.name) for item in searchMedia($viewValue)"
typeahead-on-select="selectFromSearch($item)" typeahead-template-url="/streama/typeahead--media.htm" typeahead-loading="baseData.loading"/>
<span class="form-control-feedback ion-android-search" aria-hidden="true"></span>
</div>
</li>
<sec:ifLoggedIn>
<li><a ui-sref="dash">{{'DASHBOARD.TITLE' | translate}}</a></li>
</sec:ifLoggedIn>

<sec:ifAnyGranted roles="ROLE_CONTENT_MANAGER">
<li><a ui-sref="admin.shows">{{'MANAGE_CONTENT' | translate}}</a></li>
</sec:ifAnyGranted>

<sec:ifAnyGranted roles="ROLE_ADMIN">
<li><a ui-sref="settings.settings">{{'ADMIN' | translate}}</a></li>
</sec:ifAnyGranted>

<sec:ifLoggedIn>
<li>
<div class="btn-group" style="margin: 4px 0;">
<button class="btn btn-primary pull-right" ng-click="loginUser()">{{'LOGIN' | translate}}</button>
</div>
</li>
</sec:ifLoggedIn>
</ul>
</div>

<i class="ion-navicon navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1"></i>
</header>

0 comments on commit 44a7377

Please sign in to comment.