Changeset 976


Ignore:
Timestamp:
Oct 21, 2010, 5:28:04 PM (6 years ago)
Author:
robert@…
Message:

Authentication and authorization for studies is added, according to ticket 118

Location:
trunk
Files:
39 added
20 edited

Legend:

Unmodified
Added
Removed
  • trunk/application.properties

    r972 r976  
    99app.servlet.version=2.4
    1010app.version=0.5.0
    11 plugins.aaaa=0.3.6
    1211plugins.crypto=2.0
    1312plugins.db-util=0.4
     13plugins.famfamfam=1.0.1
    1414plugins.grom=0.1.2
    1515plugins.hibernate=1.3.5
    16 plugins.jquery=1.4.3.2
     16plugins.jquery=1.4.2.5
     17plugins.jquery-ui=1.8.4.3
    1718plugins.mail=0.9
     19plugins.nadd-neutralizer=0.3
    1820plugins.oauth=0.10
    1921plugins.searchable=0.5.5.1
    2022plugins.spring-security-core=1.0.1
     23plugins.spring-security-ui=0.1.2
    2124plugins.tomcat=1.3.5
    22 plugins.webflow=1.3.5
     25plugins.webflow=1.3.4
     26
  • trunk/grails-app/conf/BaseFilters.groovy

    r776 r976  
    2929                        }
    3030                }
     31
    3132        }
    3233}
  • trunk/grails-app/conf/BootStrap.groovy

    r939 r976  
    66import org.codehaus.groovy.grails.commons.GrailsApplication
    77import grails.util.GrailsUtil
    8 import org.nmcdsp.plugins.aaaa.SecUser
     8import dbnp.authentication.*
    99
    1010
     
    2626                System.setProperty('user.timezone', 'CET')
    2727
    28                 def user = SecUser.findByUsername('user') ?: new SecUser(
     28                def adminRole = SecRole.findByAuthority( 'ROLE_ADMIN' ) ?: new SecRole( authority: 'ROLE_ADMIN' ).save()
     29
     30                def user = SecUser.findByUsername('user') ?: new SecUser(
    2931                           username: 'user',
    30                            password: springSecurityService.encodePassword('useR123!'),
    31                            enabled: true).save(failOnError: true)
     32                           password: springSecurityService.encodePassword( 'useR123!', 'user' ),
     33                           email: 'user@dbnp.org',
     34                           userConfirmed: true, adminConfirmed: true).save(failOnError: true)
    3235
    3336                def userAdmin = SecUser.findByUsername('admin') ?: new SecUser(
    3437                                username: 'admin',
    35                                 password: springSecurityService.encodePassword('admiN123!'),
    36                                 enabled: true).save(failOnError: true)
     38                                password: springSecurityService.encodePassword( 'admiN123!', 'admin' ),
     39                                email: 'admin@dbnp.org',
     40                                userConfirmed: true, adminConfirmed: true).save(failOnError: true)
     41
     42                // Make the admin user an administrator
     43                SecUserSecRole.create userAdmin, adminRole, true
    3744
    3845                def userTest = SecUser.findByUsername('test') ?: new SecUser(
    3946                                username: 'test',
    40                                 password: springSecurityService.encodePassword('testT123!'),
    41                                 enabled: true).save(failOnError: true)
    42                
    43                 println "Done with SpringSecurity bootstrap, created [user, admin, test]."
     47                                password: springSecurityService.encodePassword( 'useR123!', 'test' ),
     48                                email: 'test@dbnp.org',
     49                            userConfirmed: true, adminConfirmed: true).save(failOnError: true)
     50
     51                println "Done with SpringSecurity bootstrap, created [user, admin, test]."
    4452
    4553                // If there are no templates yet in the database
     
    8189                                } else {
    8290                                        // general study boostrapping
    83                                         BootStrapStudies.addExampleStudies(user)
     91                                        BootStrapStudies.addExampleStudies(user, userAdmin)
    8492                                }
    8593                        }
  • trunk/grails-app/conf/BootStrapStudies.groovy

    r948 r976  
    2121         */
    2222
    23         public static void addExampleStudies(org.nmcdsp.plugins.aaaa.SecUser owner) {
     23        public static void addExampleStudies(dbnp.authentication.SecUser owner, dbnp.authentication.SecUser otherUser ) {
    2424
    2525                // Look up the used ontologies which should be in the database by now
     
    171171                        ecCode:"2007117.c",
    172172                        startDate: Date.parse('yyyy-MM-dd','2008-01-02'),
    173                         owner: owner
     173                        owner: owner,
     174                        readers: [otherUser]
    174175                ).with { if (!validate()) { errors.each { println it} } else save()}
    175176
     
    378379                        ecCode:"unknown",
    379380                        startDate: Date.parse('yyyy-MM-dd','2008-01-14'),
    380                         owner: owner
     381                        owner: owner,
     382                        writers: [otherUser]
    381383                )
    382384                .setFieldValue( 'Description', "Human study performed at RRI; centres involved: RRI, IFR, TUM, Maastricht U." )
  • trunk/grails-app/conf/Config.groovy

    r936 r976  
    157157
    158158// Needed for the Spring Security Core plugin:
    159 grails.plugins.springsecurity.userLookup.userDomainClassName = 'org.nmcdsp.plugins.aaaa.SecUser'
    160 grails.plugins.springsecurity.userLookup.authorityJoinClassName = 'org.nmcdsp.plugins.aaaa.SecUserSecRole'
    161 grails.plugins.springsecurity.authority.className = 'org.nmcdsp.plugins.aaaa.SecRole'
     159grails.plugins.springsecurity.userLookup.userDomainClassName = 'dbnp.authentication.SecUser'
     160grails.plugins.springsecurity.userLookup.authorityJoinClassName = 'dbnp.authentication.SecUserSecRole'
     161grails.plugins.springsecurity.authority.className = 'dbnp.authentication.SecRole'
     162grails.plugins.springsecurity.password.algorithm = 'SHA-256'
     163grails.plugins.springsecurity.password.encodeHashAsBase64 = true
     164grails.plugins.springsecurity.dao.reflectionSaltSourceProperty = 'username' // Use the persons username as salt for encryption
     165grails.plugins.springsecurity.securityConfigType = grails.plugins.springsecurity.SecurityConfigType.Annotation
     166
     167// Make sure the different controllers provided by springsecurity.ui are only accessible by administrators
     168// NB: the RegisterController is used for forgotten passwords. It should be accessible by anyone
     169grails.plugins.springsecurity.controllerAnnotations.staticRules = [
     170    '/user/**': ['ROLE_ADMIN'],
     171    '/role/**': ['ROLE_ADMIN'],
     172    '/aclclass/**': ['ROLE_ADMIN'],
     173    '/aclentry/**': ['ROLE_ADMIN'],
     174    '/aclobjectidentity/**': ['ROLE_ADMIN'],
     175    '/aclsid/**': ['ROLE_ADMIN'],
     176    '/persistentlogin/**': ['ROLE_ADMIN'],
     177    '/registrationcode/**': ['ROLE_ADMIN'],
     178    '/requestmap/**': ['ROLE_ADMIN'],
     179    '/securityinfo/**': ['ROLE_ADMIN']
     180]
     181
     182
     183// Needed for sending emails
     184grails {
     185    mail {
     186        host = "smtp.gmail.com"
     187        port = 465
     188        username = "gscfproject@gmail.com"
     189        password = "gscf2010"
     190        props = [
     191            "mail.smtp.auth":"true",
     192            "mail.smtp.socketFactory.port": '465',
     193            "mail.smtp.socketFactory.class": "javax.net.ssl.SSLSocketFactory",
     194            "mail.smtp.socketFactory.fallback": "false"
     195        ]
     196    }
     197}
    162198
    163199// The OAuth consumer key and secret variables are currently replaced by Jeroen's Continuous Integration script.
     
    173209        }
    174210}
     211
  • trunk/grails-app/controllers/RestController.groovy

    r963 r976  
    1818import dbnp.studycapturing.Study
    1919import dbnp.studycapturing.Assay
    20 import org.nmcdsp.plugins.aaaa.SecUser
     20import dbnp.authentication.SecUser
    2121import grails.converters.*
    2222import nl.metabolomicscentre.dsp.http.BasicAuthentication
  • trunk/grails-app/controllers/dbnp/importer/ImporterController.groovy

    r959 r976  
    3131import grails.converters.JSON
    3232import org.apache.poi.hssf.usermodel.HSSFWorkbook
    33 
     33import grails.plugins.springsecurity.Secured
     34
     35@Secured(['IS_AUTHENTICATED_REMEMBERED'])
    3436class ImporterController {
    3537    def ImporterService   
  • trunk/grails-app/controllers/dbnp/studycapturing/StudyController.groovy

    r959 r976  
    22
    33import grails.converters.*
     4import grails.plugins.springsecurity.Secured
     5
    46
    57/**
     
    79 */
    810class StudyController {
    9 
     11    def AuthenticationService
     12   
    1013    //static allowedMethods = [save: "POST", update: "POST", delete: "POST"]
    1114
     
    1417    }
    1518
     19    /**
     20     * Shows all studies where the user has access to
     21     */
    1622    def list = {
    17         params.max = Math.min(params.max ? params.int('max') : 10, 100)
    18         [studyInstanceList: Study.list(params), studyInstanceTotal: Study.count()]
     23
     24        def user = AuthenticationService.getLoggedInUser()
     25        def max = Math.min(params.max ? params.int('max') : 10, 100)
     26
     27        def c = Study.createCriteria()
     28
     29        def studies
     30        if( user == null ) {
     31            studies = c.list {
     32                maxResults(max)
     33                and {
     34                    eq( "published", true )
     35                    eq( "publicstudy", true )
     36                }
     37            }
     38        } else {
     39            studies = c.list {
     40                maxResults(max)
     41                or {
     42                    eq( "owner", user )
     43                    writers {
     44                        eq( "id", user.id )
     45                    }
     46                    and {
     47                        readers {
     48                            eq( "id", user.id )
     49                        }
     50                        eq( "published", true )
     51                    }
     52                }
     53            }
     54        }
     55       
     56        [studyInstanceList: studies, studyInstanceTotal: studies.count()]
     57    }
     58
     59    /**
     60     * Shows studies for which the logged in user is the owner
     61     */
     62    @Secured(['IS_AUTHENTICATED_REMEMBERED'])
     63    def myStudies = {
     64        def user = AuthenticationService.getLoggedInUser()
     65        def max = Math.min(params.max ? params.int('max') : 10, 100)
     66
     67        def studies = Study.findAllByOwner(user);
     68        render( view: "list", model: [studyInstanceList: studies, studyInstanceTotal: studies.count()] )
    1969    }
    2070
     
    4393        }
    4494        else {
     95            // Check whether the user may see this study
     96            def loggedInUser = AuthenticationService.getLoggedInUser()
     97            if( !studyInstance.canRead(loggedInUser) ) {
     98                flash.message = "You have no access to this study"
     99                redirect(action: "list")
     100            }
     101
    45102            // The study instance is packed into an array, to be able to
    46103            // use the same view for showing the study and comparing multiple
    47104            // studies
    48             [studyList: [ studyInstance ], multipleStudies: false ]
    49         }
    50     }
    51 
    52         def showByToken = {
     105            [studyList: [ studyInstance ], multipleStudies: false, loggedInUser: loggedInUser ]
     106        }
     107    }
     108
     109    def showByToken = {
    53110        def studyInstance = Study.findByCode(params.id)
    54111        if (!studyInstance) {
     
    57114        }
    58115        else {
     116            // Check whether the user may see this study
     117            def loggedInUser = AuthenticationService.getLoggedInUser()
     118            if( !studyInstance.canRead(loggedInUser) ) {
     119                flash.message = "You have no access to this study"
     120                redirect(action: "list")
     121            }
     122
    59123            redirect(action: "show", id: studyInstance.id)
    60124        }
  • trunk/grails-app/controllers/dbnp/studycapturing/WizardController.groovy

    r959 r976  
    55// Grails convertors is imported in order to create JSON objects
    66import grails.converters.*
     7import grails.plugins.springsecurity.Secured
     8import dbnp.authentication.AuthenticationService
     9import dbnp.authentication.SecUser
    710
    811
     
    1215 * The wizard controller handles the handeling of pages and data flow
    1316 * through the study capturing wizard.
     17 *
     18 * This controller is only accessible by logged in users.
    1419 *
    1520 * @author Jeroen Wesbeek
     
    2227 * $Date$
    2328 */
     29@Secured(['IS_AUTHENTICATED_REMEMBERED'])
    2430class WizardController {
     31        def AuthenticationService
     32       
    2533        /**
    2634         * index method, redirect to the webflow
     
    790798                                        // save study
    791799                                        println ".saving study"
     800
     801                                        // Make sure the owner of the study is set right
     802                                        flow.study.owner = AuthenticationService.getLoggedInUser()
     803                                       
    792804                                        if (!flow.study.save(flush:true)) {
    793805                                                this.appendErrors(flow.study, flash.errors)
     
    846858                try {
    847859                        // load study
    848                         flow.study = (params.studyid) ? Study.findById( params.studyid ) : Study.findByTitle( params.study )
    849 
     860                        def study = (params.studyid) ? Study.findById( params.studyid ) : Study.findByTitle( params.study )
     861
     862                        // Check whether the user is allowed to edit this study. If it is not allowed
     863                        // the used should had never seen a link to this page, so he should never get
     864                        // here. That's why we just return false
     865                        if( !study.canWrite(AuthenticationService.getLoggedInUser()) ) {
     866                            return false;
     867                        }
     868
     869                        flow.study = study
    850870                        // set 'quicksave' variable
    851871                        flow.quickSave = true
     
    903923                // handle contacts
    904924                handleContacts(flow, flash, params)
     925
     926                // handle users (readers, writers)
     927                handleUsers(flow, flash, params, 'readers')
     928                handleUsers(flow, flash, params, 'writers')
     929
     930                // handle public checkbox
     931                if( params.get( "publicstudy" ) ) {
     932                    flow.study.publicstudy = params.get( "publicstudy" )
     933                }
    905934
    906935                // validate the study
     
    10161045
    10171046        }
     1047
     1048        /**
     1049         * re-usable code for handling contacts form data in a web flow
     1050         * @param Map LocalAttributeMap (the flow scope)
     1051         * @param Map localAttributeMap (the flash scope)
     1052         * @param Map GrailsParameterMap (the flow parameters = form data)
     1053         * @param String    'readers' or 'writers'
     1054         * @return boolean
     1055         */
     1056        def handleUsers(flow, flash, params, type) {
     1057                def users = []
     1058
     1059                if( type == "readers" ) {
     1060                    users = flow.study.readers ?: []
     1061                } else if( type == "writers" ) {
     1062                    users = flow.study.writers ?: []
     1063                }
     1064
     1065                // Check the ids of the contacts that should be attached
     1066                // to this study. If they are already attached, keep 'm. If
     1067                // studies are attached that are not in the selected (i.e. the
     1068                // user deleted them), remove them
     1069
     1070                // Users are saved as user_id
     1071                def userIDs = params.get( type + '_ids')
     1072                if (userIDs) {
     1073                        // Find the individual IDs and make integers
     1074                        userIDs = userIDs.split(',').collect { Integer.parseInt(it, 10) }
     1075
     1076                        // First remove the publication that are not present in the array
     1077                        users.removeAll { user -> !userIDs.find { id -> id == user.id } }
     1078
     1079                        // Add those publications not yet present in the database
     1080                        userIDs.each { id ->
     1081                                if (!users.find { user -> id == user.id }) {
     1082                                        def user = SecUser.get(id)
     1083                                        if (user) {
     1084                                            users.add(user)
     1085                                        } else {
     1086                                                println('.user with ID ' + id + ' not found in database.')
     1087                                        }
     1088                                }
     1089                        }
     1090
     1091                } else {
     1092                        println('.no users selected.')
     1093                        users.clear()
     1094                }
     1095
     1096                if( type == "readers" ) {
     1097                    if( flow.study.readers )
     1098                        flow.study.readers.clear()
     1099                    users.each { flow.study.addToReaders( it ) }
     1100                } else if( type == "writers" ) {
     1101                    if( flow.study.writers )
     1102                        flow.study.writers.clear()
     1103                       
     1104                    users.each { flow.study.addToWriters( it ) }
     1105                }
     1106
     1107        }
    10181108                                                 
    10191109        /**
  • trunk/grails-app/domain/dbnp/studycapturing/Study.groovy

    r974 r976  
    11package dbnp.studycapturing
    22
    3 import org.nmcdsp.plugins.aaaa.SecUser
     3import dbnp.authentication.SecUser
    44
    55/**
     
    1212 */
    1313class Study extends TemplateEntity {
    14         static searchable = {
    15         [only: ['title', 'Description']] // the description field will be searched only if defined in a study template
    16     }
     14        static searchable = {
     15            [only: ['title', 'Description']] // the description field will be searched only if defined in a study template
     16        }
    1717
    1818        SecUser owner           // The owner of the study. A new study is automatically owned by its creator.
     
    2929        List assays
    3030        boolean published = false // Determines whether a study is private (only accessable by the owner and writers) or published (also visible to readers)
    31 
     31        boolean publicstudy = false  // Determines whether anonymous users are allowed to see this study. This has only effect when published = true
     32       
    3233        static hasMany = [             
    3334                subjects: Subject,
     
    3839                assays: Assay,
    3940                persons: StudyPerson,
    40                 publications: Publication
     41                publications: Publication,
     42                readers: SecUser,
     43                writers: SecUser
    4144        ]
    4245
     
    5457                // Workaround for bug http://jira.codehaus.org/browse/GRAILS-6754
    5558                templateTextFields type: 'text'
    56                 owner column: "studyowner"
    57                 title column: "studytitle"
    58                 code column: "studycode"
    59                 subjects column: "studysubjects"
    60                 events column: "studyevents"
    61                 samplingEvents column: "studysamplingevents"
    62                 eventGroups column: "studyeventgroups"
    63                 samples column: "studysamples"
    64                 assays column: "studyassays"
    6559        }
    6660
     
    407401                return msg
    408402        }
     403
     404    /**
     405     * Returns true if the given user is allowed to read this study
     406     */
     407    public boolean canRead(SecUser loggedInUser) {
     408        // Anonymous readers are only given access when published and public
     409        if( loggedInUser == null ) {
     410            return this.publicstudy && this.published;
     411        }
     412
     413        // Owners and writers are allowed to read this study
     414        if( this.owner == loggedInUser || this.writers.contains(loggedInUser) ) {
     415            return true
     416        }
     417           
     418        // Readers are allowed to read this study when it is published
     419        if( this.readers.contains(loggedInUser) && this.published ) {
     420            return true
     421        }
     422       
     423        return false
     424    }
     425
     426    /**
     427     * Returns true if the given user is allowed to write this study
     428     */
     429    public boolean canWrite(SecUser loggedInUser) {
     430        if( loggedInUser == null ) {
     431            return false;
     432        }
     433        return this.owner == loggedInUser || this.writers.contains(loggedInUser)
     434    }
     435
     436    /**
     437     * Returns true if the given user is the owner of this study
     438     */
     439    public boolean isOwner(SecUser loggedInUser) {
     440        if( loggedInUser == null ) {
     441            return false;
     442        }
     443        return this.owner == loggedInUser
     444    }
     445
    409446}
  • trunk/grails-app/domain/dbnp/studycapturing/Template.groovy

    r961 r976  
    11package dbnp.studycapturing
    22
    3 import org.nmcdsp.plugins.aaaa.SecUser
     3import dbnp.authentication.SecUser
    44
    55/**
     
    3939
    4040        static mapping = {
    41                 name column:"templatename"
    42                 description column:"templatedescription"
    43                 entity column:"templateentity"
    44                 owner column:"templateowner"
    45                 fields column:"templatefields"
    4641        }
    4742
  • trunk/grails-app/domain/dbnp/studycapturing/TemplateEntity.groovy

    r961 r976  
    7070                // Make sure that the text fields are really stored as TEXT, so that those Strings can have an arbitrary length.
    7171                templateTextFields type: 'text'
    72 
    73                 template column:"templateentitytemplate"
    7472        }
    7573
  • trunk/grails-app/taglib/dbnp/studycapturing/WizardTagLib.groovy

    r959 r976  
    33import org.codehaus.groovy.grails.plugins.web.taglib.JavascriptTagLib
    44import dbnp.studycapturing.*
     5import dbnp.authentication.SecUser
    56import dbnp.data.*
    67import cr.co.arquetipos.crypto.Blowfish
     
    1920 */
    2021class WizardTagLib extends JavascriptTagLib {
     22
     23        def AuthenticationService
     24       
    2125        // define the tag namespace (e.g.: <wizard:action ... />
    2226        static namespace = "wizard"
     
    649653         */
    650654        def studySelect = { attrs ->
    651                 // for now, just fetch all studies
    652                 attrs.from = Study.findAll()
     655                // Find all studies the user has access to
     656                def user = AuthenticationService.getLoggedInUser()
     657
     658                def c = Study.createCriteria()
     659                attrs.from = c.list {
     660                    or {
     661                        eq( "owner", user )
     662                        writers {
     663                            eq( "id", user.id )
     664                        }
     665                    }
     666                }
    653667
    654668                // got a name?
     
    13111325                out << "Role: " + select(selectAttrs)
    13121326        }
     1327
     1328
     1329        def UserSelectElement = { attrs, body ->
     1330                // render list with publications currently available
     1331                baseElement.call(
     1332                        '_userList',
     1333                        attrs,
     1334                        body
     1335                )
     1336
     1337                attrs.description = '';
     1338
     1339                // render 'Add user button'
     1340                baseElement.call(
     1341                        '_userAddButton',
     1342                        attrs,
     1343                        body
     1344                )
     1345        }
     1346
     1347        /**
     1348         * Renders an input box for publications
     1349         */
     1350        def userSelect = { attrs, body ->
     1351                if (attrs.get('value') == null) {
     1352                        attrs.value = [];
     1353                }
     1354                if (attrs.get('description') == null) {
     1355                        attrs.description = '';
     1356                }
     1357               
     1358                out << '<form id="' + attrs.name + '_form" onSubmit="return false;">';
     1359                out << select(
     1360                        name: attrs.get("name"),
     1361                        value: '',
     1362                        from: SecUser.list(),
     1363                        optionValue: 'username',
     1364                        optionKey: 'id',
     1365                        style: 'width: 400px;'
     1366                );
     1367                out << '</form>';
     1368        }
     1369
     1370        def _userList = { attrs, body ->
     1371                def display_none = 'none';
     1372                if (!attrs.get('value') || attrs.get('value').size() == 0) {
     1373                        display_none = 'inline';
     1374                }
     1375
     1376                // Add a unordered list
     1377                out << '<ul class="user_list" id="' + attrs.name + '_list">';
     1378
     1379                out << '<li>';
     1380                out << '<span class="user_none" id="' + attrs.name + '_none" style="display: ' + display_none + ';">';
     1381                out << '-';
     1382                out << '</span>';
     1383                out << '</li>';
     1384
     1385                out << '</ul>';
     1386
     1387                // Add the publications using javascript
     1388                out << '<script type="text/javascript">'
     1389                if (attrs.get('value') && attrs.get('value').size() > 0) {
     1390                        def i = 0;
     1391                        attrs.get('value').each {
     1392                                out << 'showUser( ';
     1393                                out << '  "' + attrs.name + '",';
     1394                                out << '  ' + it.id + ',';
     1395                                out << '  "' + it.username + '",';
     1396                                out << '  ' + i++;
     1397                                out << ');';
     1398                        }
     1399                }
     1400                out << '</script>';
     1401
     1402                def ids;
     1403                if (attrs.get('value') && attrs.get('value').size() > 0) {
     1404                        ids = attrs.get('value').id.join(',')
     1405                } else {
     1406                        ids = '';
     1407                }
     1408                out << '<input type="hidden" name="' + attrs.name + '_ids" value="' + ids + '" id="' + attrs.name + '_ids">';
     1409        }
     1410
     1411        def _userAddButton = { attrs, body ->
     1412
     1413                // Output the dialog for the publications
     1414                out << '<div id="' + attrs.name + '_dialog">';
     1415                out << '<p>Select a user from the database.</p>';
     1416                out << userSelect(attrs, body);
     1417                out << '</div>';
     1418                out << '<script type="text/javascript">';
     1419                out << '  createUserDialog( "' + attrs.name + '" );'
     1420                out << '</script>';
     1421
     1422                out << '<input type="button" onClick="openUserDialog(\'' + attrs.name + '\' );" value="Add User">';
     1423        }
     1424
    13131425}
  • trunk/grails-app/views/common/_login_panel.gsp

    r959 r976  
    1414                                        <h1>Member Login</h1>
    1515                                        <label class="grey" for="username">Username:</label>
    16                                         <input class="field" type="text" name="j_username" id="username" value="${username}" size="23" />
     16                                        <input class="field" type="text" name="j_username" id="j_username" value="${username}" size="23" />
    1717                                        <label class="grey" for="password">Password:</label>
    1818                                        <input class="field" type="password" name="j_password" id="password" size="23" />
    19                                         <label><input type='checkbox' class='chk' name='${rememberMeParameter}' id='remember_me'
    20                                         <g:if test='${hasCookie}'>checked='checked'</g:if> /></label>
    21                 <div class="clear"></div>
     19                                        <label><input type='checkbox' class='chk' name='_spring_security_remember_me' id='remember_me'
     20                                        <g:if test='${hasCookie}'>checked='checked'</g:if> /> Remember me</label>
     21                                        <div class="clear"></div>
    2222                                        <input type="submit" name="submit" value="Login" class="bt_login" />
    23                                         <a class="lost-pwd" href="<g:createLink url="[action:'passwordReminder',controller:'auth']" class="lost-pwd" />">Lost your password?</a>
     23                                        <a class="lost-pwd" href="<g:createLink url="[action:'forgotPassword',controller:'register']" class="lost-pwd" />">Lost your password?</a>
    2424                                </g:form>
    2525                        </div>
    2626                        <div class="left right">
    27                                 <g:form url="[action:'signUp',controller:'auth']" class="clearfix">
     27                          <g:form url="[action:'add',controller:'userRegistration']" class="clearfix">
    2828                                        <input type="hidden" name="targetUri" value="${targetUri}" />
    2929                                        <h1>Not a member yet? Sign Up!</h1>
    3030                                        <label class="grey" for="signup">Username:</label>
    31                                         <input class="field" type="text" name="signup" id="signup" value="${username}" size="23" />
     31                                        <input class="field" type="text" name="username" id="username" value="${username}" size="23" />
    3232                                        <label class="grey" for="email">Email:</label>
    3333                                        <input class="field" type="text" name="email" id="email" value="${email}" size="23" />
    34                                         <label>A password will be e-mailed to you.</label>
     34                                        <label>A password will be e-mailed to you</label>
     35
    3536                                        <input type="submit" name="submit" value="Register" class="bt_register" />
    36                                 </g:form>
     37                            </g:form>
    3738                        </div>
    3839                </div>
     
    4142                <ul class="login">
    4243                        <li class="left">&nbsp;</li>
    43                         <li>Hello <authentication:isLoggedIn><authentication:loggedInUsername/></authentication:isLoggedIn>
    44                         <authentication:isNotLoggedIn>Guest</authentication:isNotLoggedIn>!</li>
     44                        <li>Hello <sec:ifLoggedIn><sec:username/></sec:ifLoggedIn>
     45                        <sec:ifNotLoggedIn>Guest</sec:ifNotLoggedIn>!</li>
    4546                        <li class="sep">|</li>
    4647                        <li id="toggle">
    47                         <authentication:isLoggedIn><g:link controller="logout" action="index">sign out</g:link></authentication:isLoggedIn>
    48                                 <authentication:isNotLoggedIn>
    49                                 <a id="open" class="open" href="#">Log In | Register</a>
    50                                 <a id="close" style="display: none;" class="close" href="#">Close Panel</a>
    51                                 </authentication:isNotLoggedIn>
     48                        <sec:ifLoggedIn><g:link controller="logout" action="index">sign out</g:link></sec:ifLoggedIn>
     49                            <sec:ifNotLoggedIn>
     50                            <a id="open" class="open" href="#">Log In | Register</a>
     51                            <a id="close" style="display: none;" class="close" href="#">Close Panel</a>
     52                            </sec:ifNotLoggedIn>
    5253                        </li>
    5354            <li class="right">&nbsp;</li>
  • trunk/grails-app/views/common/_topnav.gsp

    r959 r976  
    33    <ul class="topnav">
    44     <li><g:link controller="home" action="index">Home</g:link></li>
    5 <n:isLoggedIn>
    6      <li><g:link controller="study" action="list">My studies</g:link></li>
    7 </n:isLoggedIn>     <li>
     5<sec:ifLoggedIn>
     6     <li><g:link controller="study" action="myStudies">My studies</g:link></li>
     7</sec:ifLoggedIn>     <li>
    88      <a href="#">Studies</a>
    99      <ul class="subnav">
     
    3838     </li>
    3939        </g:if>
    40 <n:isAdministrator>
     40   <sec:ifAllGranted roles="ROLE_ADMIN">
    4141     <li>
    4242          <a href="#">User administation</a>
    4343          <ul class="subnav">
    44             <li><g:link controller="admins" action="index" class="icon icon_user_suit">Manage Administrators</g:link></li>
    45             <li><g:link controller="user" action="list" class="icon icon_user">List Users</g:link></li>
     44            <li><g:link controller="user" class="icon icon_user">List Users</g:link></li>
    4645            <li><g:link controller="user" action="create" class="icon icon_user_add">Create User</g:link></li>
    47             <li><g:link controller="role" action="list" class="icon icon_cog">List Roles</g:link></li>
     46            <li><g:link controller="role" class="icon icon_cog">List Roles</g:link></li>
    4847            <li><g:link controller="role" action="create" class="icon icon_cog_add">Create Role</g:link></li>
    49             <li><g:link controller="group" action="list" class="icon icon_group">List Groups</g:link></li>
    50             <li><g:link controller="group" action="create" class="icon icon_group_add">Create Group</g:link></li>
    51             <li><g:link controller="auth" action="logout" class="icon icon_cross">Sign out</g:link></li>
     48            <li><g:link controller="logout" class="icon icon_cross">Sign out</g:link></li>
    5249          </ul>
    5350     </li>
    54 </n:isAdministrator>
     51   </sec:ifAllGranted>
    5552    </ul>
    5653    <!-- /TOPNAV //-->
  • trunk/grails-app/views/study/list.gsp

    r959 r976  
    7777    </div>
    7878    <div class="buttons">
    79                 <span class="button"><g:link class="create" controller="wizard" params="[jump:'create']"><g:message code="default.new.label" args="[entityName]" /></g:link></span>
     79      <sec:ifLoggedIn>
     80        <span class="button"><g:link class="create" controller="wizard" params="[jump:'create']"><g:message code="default.new.label" args="[entityName]" /></g:link></span>
     81      </sec:ifLoggedIn>
    8082    </div>
    8183    <div class="paginateButtons">
  • trunk/grails-app/views/study/show.gsp

    r968 r976  
    273273              </g:each>
    274274            </tr>
    275                           <tr>
    276                                 <td>Owner</td>
    277                                 <g:each in="${studyList}" var="studyInstance">
    278                                   <td>
    279                                         <g:if test="${studyInstance.owner}">
    280                                                 <g:link controller="user" action="show" id="${studyInstance.owner.id}">${studyInstance.owner?.encodeAsHTML()}</g:link>
    281                                         </g:if>
    282                                         <g:else>
    283                                          -
    284                                         </g:else>
    285                                   </td>
    286                                 </g:each>
    287                           </tr>
    288275            <tr>
    289               <td>Members</td>
     276              <td>Public</td>
    290277              <g:each in="${studyList}" var="studyInstance">
    291278                <td>
    292                   <% /* <g:if test="${studyInstance.getAllMemberUsers()==0}">
     279                   ${studyInstance.publicstudy}
     280                </td>
     281              </g:each>
     282            </tr>
     283            <tr>
     284              <td>Owner</td>
     285              <g:each in="${studyList}" var="studyInstance">
     286                <td>
     287                   ${studyInstance.owner.username}
     288                </td>
     289              </g:each>
     290            </tr>
     291            <tr>
     292              <td>Readers</td>
     293              <g:each in="${studyList}" var="studyInstance">
     294                <td>
     295                  <g:if test="${studyInstance.readers.size() == 0}">
    293296                    -
    294297                  </g:if>
    295298                  <g:else>
    296                     <g:each in="${studyInstance.getAllMemberUsers()}" var="memberuser" status="i">
    297                       <g:if test="${i > 0}">, </g:if>
    298                       <g:link controller="user" action="show" id="${memberuser.id}">${memberuser?.encodeAsHTML()}</g:link>
    299                     </g:each>
     299                    ${studyInstance.readers.username.join( ", " )}
    300300                  </g:else>
    301                   */ %>
     301                </td>
     302              </g:each>
     303            </tr>
     304            <tr>
     305              <td>Writers</td>
     306              <g:each in="${studyList}" var="studyInstance">
     307                <td>
     308                  <g:if test="${studyInstance.writers.size()==0}">
     309                    -
     310                  </g:if>
     311                  <g:else>
     312                    ${studyInstance.writers.username.join( ", " )}
     313                  </g:else>
    302314                </td>
    303315              </g:each>
     
    845857          <g:set var="studyInstance" value="${studyList[0]}" />
    846858          <g:hiddenField name="id" value="${studyInstance?.id}" />
    847           <span class="button"><g:link class="edit" controller="wizard" params="[jump:'edit']" id="${studyInstance?.id}">${message(code: 'default.button.edit.label', default: 'Edit')}</g:link></span>
    848           <span class="button"><g:actionSubmit class="delete" action="delete" value="${message(code: 'default.button.delete.label', default: 'Delete')}" onclick="return confirm('${message(code: 'default.button.delete.confirm.message', default: 'Are you sure?')}');" /></span>
    849         </g:if>
     859          <g:if test="${studyInstance.canWrite(loggedInUser)}">
     860            <span class="button"><g:link class="edit" controller="wizard" params="[jump:'edit']" id="${studyInstance?.id}">${message(code: 'default.button.edit.label', default: 'Edit')}</g:link></span>
     861          </g:if>
     862          <g:if test="${studyInstance.isOwner(loggedInUser)}">
     863            <span class="button"><g:actionSubmit class="delete" action="delete" value="${message(code: 'default.button.delete.label', default: 'Delete')}" onclick="return confirm('${message(code: 'default.button.delete.confirm.message', default: 'Are you sure?')}');" /></span>
     864          </g:if>
     865          </g:if>
    850866        <span class="button"><g:link class="backToList" action="list">Back to list</g:link></span>
    851867      </g:form>
  • trunk/grails-app/views/wizard/pages/_study.gsp

    r959 r976  
    3131        <wizard:publicationSelectElement name="publication" value="${study?.publications}" />
    3232        <wizard:contactSelectElement name="contacts" value="${study?.persons}" />
    33         </g:if>
     33
     34        <br />
     35        <div class="element">
     36          <div class="description">Public </div>
     37          <div class="input"><g:checkBox name="publicstudy" value="${study?.publicstudy}" /></div>
     38        </div>
     39
     40        <wizard:userSelectElement name="readers" description="Readers" value="${study?.readers}" />
     41        <wizard:userSelectElement name="writers" description="Writers" value="${study?.writers}" />
     42
     43        </g:if>
    3444
    3545</wizard:pageContent>
  • trunk/web-app/css/wizard.css

    r959 r976  
    385385}
    386386
    387 .wizard ul.publication_list, .wizard ul.contact_list {
     387.wizard ul.publication_list, .wizard ul.contact_list, .wizard ul.user_list {
    388388    list-style-type: none;
    389389    margin: -10px 0 0 255px;
     
    391391}
    392392
    393 .wizard ul.publication_list li, .wizard ul.contact_list li {
     393.wizard ul.publication_list li, .wizard ul.contact_list li, .wizard ul.user_list li {
    394394    margin-left: 0px;
    395395    padding: 4px 6px;
  • trunk/web-app/js/wizard.js

    r959 r976  
    382382}
    383383
    384 
    385384/*************************************************
    386385 *
     
    674673}
    675674
     675/*************************************************
     676 *
     677 * Functions for adding users (readers or writers) to the study
     678 *
     679 ************************************************/
     680
     681/**
     682 * Adds a user to the study using javascript
     683 */
     684function addUser( element_id ) {
     685    /* Find publication ID and add to form */
     686    id = parseInt( $("#" + element_id + "_form select").val() );
     687
     688    // Put the ID in the array, but only if it does not yet exist
     689    var ids = getUserIds( element_id );
     690
     691    if( $.inArray (id, ids ) == -1 ) {
     692        ids[ ids.length ] = id;
     693        $( '#' + element_id + '_ids' ).val( ids.join( ',' ) );
     694
     695        // Show the title and a remove button
     696        showUser( element_id, id, $("#" + element_id + "_form select option:selected").text(), ids.length - 1 );
     697
     698        // Hide the 'none box'
     699        $( '#' + element_id + '_none' ).css( 'display', 'none' );
     700    }
     701
     702    return false;
     703}
     704
     705/**
     706 * Removes a user from the study using javascript
     707 * N.B. The deletion must be handled in grails when the form is submitted
     708 */
     709function removeUser( element_id, id ) {
     710    var ids = getUserIds( element_id );
     711    if( $.inArray(id, ids ) != -1 ) {
     712        // Remove the ID
     713        ids.splice($.inArray(id, ids ), 1);
     714        $( '#' + element_id + '_ids' ).val( ids.join( ',' ) );
     715
     716        // Remove the title from the list
     717        var li = $( "#" + element_id + '_item_' + id );
     718        if( li ) {
     719            li.remove();
     720        }
     721
     722        // Show the 'none box' if needed
     723        if( ids.length == 0 ) {
     724            $( '#' + element_id + '_none' ).css( 'display', 'inline' );
     725        }
     726
     727    }
     728}
     729
     730/**
     731 * Returns an array of user IDs currently attached to the study
     732 * The array contains integers
     733 */
     734function getUserIds( element_id ) {
     735    var ids = $( '#' + element_id + '_ids' ).val();
     736    if( ids == "" ) {
     737        return new Array();
     738    } else {
     739        ids_array = ids.split( ',' );
     740        for( var i = 0; i < ids_array.length; i++ ) {
     741            ids_array[ i ] = parseInt( ids_array[ i ] );
     742        }
     743
     744        return ids_array;
     745    }
     746}
     747
     748/**
     749 * Shows a publication on the screen
     750 */
     751function showUser( element_id, id, username, nr ) {
     752    var deletebutton = document.createElement( 'img' );
     753    deletebutton.className = 'famfamfam delete_button';
     754    deletebutton.setAttribute( 'alt', 'remove this user' );
     755    deletebutton.setAttribute( 'src', baseUrl + '/images/icons/famfamfam/delete.png' );
     756    deletebutton.onclick = function() { removeUser(  element_id, id ); return false; };
     757
     758    var titleDiv = document.createElement( 'div' );
     759    titleDiv.className = 'username' ;
     760    titleDiv.appendChild( document.createTextNode( username ) );
     761
     762    var li = document.createElement( 'li' );
     763    li.setAttribute( 'id', element_id + '_item_' + id );
     764    li.className = nr % 2 == 0 ? 'even' : 'odd';
     765    li.appendChild( deletebutton );
     766    li.appendChild( titleDiv );
     767
     768    $( '#' + element_id + '_list' ).append( li );
     769}
     770
     771/**
     772 * Creates the dialog for searching a publication
     773 */
     774function createUserDialog( element_id ) {
     775    /* Because of the AJAX loading of this page, the dialog will be created
     776     * again, when the page is reloaded. This raises problems when reading the
     777     * values of the selected publication. For that reason we check whether the
     778     * dialog already exists
     779     */
     780    if( $( "." + element_id + "_user_dialog" ).length == 0 ) {
     781        $("#" + element_id + "_dialog").dialog({
     782            title   : "Add user",
     783            autoOpen: false,
     784            width   : 800,
     785            height  : 400,
     786            modal   : true,
     787            dialogClass : element_id + "_user_dialog",
     788            position: "center",
     789            buttons : {
     790               Add  : function() { addUser( element_id ); $(this).dialog("close"); },
     791               Close  : function() { $(this).dialog("close"); }
     792            },
     793            close   : function() {
     794                /* closeFunc(this); */
     795            }
     796        }).width(790).height(400);
     797    } else {
     798       /* If a dialog already exists, remove the new div */
     799       $("#" + element_id + "_dialog").remove();
     800    }
     801}
     802
     803/**
     804 * Opens the dialog for searching a publication
     805 */
     806function openUserDialog( element_id ) {
     807    // Empty input field
     808    var field = $( '#' + element_id );
     809    field.val( '' );
     810
     811    // Show the dialog
     812    $( '#' + element_id + '_dialog' ).dialog( 'open' );
     813    field.focus();
     814
     815    // Disable 'Add' button
     816    //enableButton( '.' + element_id + '_user_dialog', 'Add', false );
     817}
Note: See TracChangeset for help on using the changeset viewer.