source: trunk/grails-app/controllers/RestController.groovy @ 983

Last change on this file since 983 was 983, checked in by robert@…, 11 years ago

New type of authentication and authorization added to the rest controller. See ticket 118

  • Property svn:keywords set to Author Date Rev
File size: 12.2 KB
Line 
1/**
2 * RestController
3 *
4 * This controler provides a REST service.
5 * The names of the RESET resources are the same as the names of this
6 * controller's actions. E.g., the resources called getStudies simply
7 * corresponds to the action getStudies. Some of the resources are parameterized.
8 * The parameters are passed as parameters in the url and are available in the
9 * params respecting Grails' conventions. In this file, we adher to the javadoc 
10 * convention for describing parameters ("@param"), but actually we mean
11 * key-value pairs in the params object of each Grails action we comment on.
12 *
13 * @author      Jahn-Takeshi Saito
14 * @since       20100601
15 *
16 */
17
18import dbnp.studycapturing.Study
19import dbnp.studycapturing.Assay
20import dbnp.authentication.SecUser
21import grails.converters.*
22import nl.metabolomicscentre.dsp.http.BasicAuthentication
23
24
25class RestController {
26
27       /**************************************************/
28      /** Rest resources for Simple Assay Module (SAM) **/
29     /**************************************************/
30
31        def AuthenticationService       
32        def beforeInterceptor = [action:this.&auth,except:["isUser"]]
33        def credentials
34        def requestUser // = SecUser.findByName( "user" )
35
36        /**
37         * Authorization closure, which is run before executing any of the REST resource actions
38         * It fetches a consumer/token combination from the url and checks whether
39         * that is a correct and known combination
40         *
41         * @param       consumer        consumer name of the calling module
42         * @param       token           token for the authenticated user (e.g. session_id)
43         * @return true if the user is remotely logged in, false otherwise
44         */
45        private def auth() {
46                if( !AuthenticationService.isRemotelyLoggedIn( params.consumer, params.token ) ) {
47                        response.sendError(403)
48                        return false
49                } else {
50                        return true
51                }
52        }
53
54        /**
55         * REST resource for data modules.
56         * Consumer and token should be supplied via URL parameters.
57         * Determines whether the given user/password combination is a valid GSCF account.
58         *
59         * @param       consumer        consumer name of the calling module
60         * @param       token           token for the authenticated user (e.g. session_id)
61         * @return bool {"authenticated":true} when user/password is a valid GSCF account, {"authenticated":false} otherwise.
62         */
63        def isUser = {
64                boolean isUser = AuthenticationService.isRemotelyLoggedIn( params.consumer, params.token )
65                def reply = ['authenticated':isUser]
66                render reply as JSON
67        }
68
69
70        /**
71         * REST resource for data modules.
72         * Consumer and token should be supplied via URL parameters.
73         * Provide a list of all studies owned by the supplied user.
74         *
75         * @param       consumer        consumer name of the calling module
76         * @param       token           token for the authenticated user (e.g. session_id)
77         * @return JSON object list containing 'studyToken', and 'name' (title) for each study
78         */
79        def getStudies = {
80                List studies = [] 
81                Study.findAllByOwner(AuthenticationService.getRemotelyLoggedInUser( params.consumer, params.token )).each { study ->
82                        studies.push( [ 'title':study.title, 'studyToken':study.getToken()] )
83                }
84                render studies as JSON
85        }
86
87
88        /**
89         * REST resource for data modules.
90         * Consumer and token should be supplied via URL parameters.
91         * Provide a list of all subjects belonging to a study.
92         *
93         * If the user is not allowed to read the study contents, a 401 error is given
94         *
95         * @param       studyToken      String The external study id (code) of the target GSCF Study object
96         * @param       consumer        consumer name of the calling module
97         * @param       token           token for the authenticated user (e.g. session_id)
98         * @return JSON object list of subject names
99         */
100        def getSubjects = {
101                List subjects = [] 
102                if( params.studyToken ) {
103                        def id = params.studyToken
104                        def study = Study.find( "from Study as s where s.code=?", [id])
105
106                        if(study) {
107                                // Check whether the person is allowed to read the data of this study
108                                if( !study.canRead(AuthenticationService.getRemotelyLoggedInUser( params.consumer, params.token ))) {
109                                        response.sendError(401)
110                                        return false
111                                }
112
113                                study.subjects.each { subjects.push it.name }
114                        }
115                }
116                render subjects as JSON
117        }
118
119
120        /**
121         * REST resource for data modules.
122         * Consumer and token should be supplied via URL parameters.
123         * Provide a list of all assays for a given study.
124         *
125         * If the user is not allowed to read the study contents, a 401 error is given
126         *
127         * Example call of the getAssays REST resource:
128         * http://localhost:8080/gscf/rest/getAssays?studyToken=PPSH&moduleURL=http://localhost:8182/sam
129         *
130         * @param       studyToken      String The external study id (code) of the target GSCF Study object
131         * @param       moduleURL       String The base URL of the calling dbNP module
132         * @param       consumer        consumer name of the calling module
133         * @param       token           token for the authenticated user (e.g. session_id)
134         * @return list of assays in the study as JSON object list, filtered to only contain assays
135         *         for the specified module, with 'assayToken' and 'name' for each assay
136         */
137        def getAssays = {
138                List assays = [] 
139                if( params.studyToken ) {
140                        def id = params.studyToken
141                        def study = Study.find( "from Study as s where s.code=?", [id] )
142
143                        if(study) {
144                                // Check whether the person is allowed to read the data of this study
145                                if( !study.canRead(AuthenticationService.getRemotelyLoggedInUser( params.consumer, params.token ))) {
146                                        response.sendError(401)
147                                        return false
148                                }
149
150                                study.assays.each{ assay ->
151                                        if (assay.module.url.equals(params.moduleURL)) {
152                                                def map = ['name':assay.name, 'assayToken':assay.getToken()]
153                                                assays.push( map )
154                                        }
155                                }
156                        }
157                }
158                render assays as JSON
159        }
160
161
162        /**
163         * REST resource for data modules.
164         * Username and password should be supplied via HTTP Basic Authentication.
165         * Provide all samples of a given Assay. The result is an enriched list with additional information for each sample.
166         *
167         * @param       assayToken      String (assayToken of some Assay in GSCF)
168         * @param       consumer        consumer name of the calling module
169         * @param       token           token for the authenticated user (e.g. session_id)
170         * @return As a JSON object list, for each sample in that assay:
171         * @return 'name' (Sample name, which is unique)
172         * @return 'material' (Sample material)
173         * @return 'subject' (The name of the subject from which the sample was taken)
174         * @return 'event' (the name of the template of the SamplingEvent describing the sampling)
175         * @return 'startTime' (the time the sample was taken relative to the start of the study, as a string)
176         */
177        def getSamples = {
178                def items = []
179                if( params.assayToken ) {
180                        def assay = Assay.find( "from Assay as a where externalAssayID=?",[params.assayToken])
181                        if( assay )  {
182                                assay.getSamples().each { sample ->
183                                        def item = [ 
184                                                'sampleToken' : sample.name,
185                                                'material'        : sample.material?.name,
186                                                'subject'         : sample.parentSubject?.name,
187                                                'event'           : sample.parentEvent?.template?.name,
188                                                'startTime'       : sample.parentEvent?.getStartTimeString()
189                                        ]
190                                        items.push item
191                                }
192                        }
193                }
194                render items as JSON
195        }
196
197
198        /**
199         * REST resource for dbNP modules.
200         *
201         * @param       studyToken String, the external identifier of the study
202         * @param       consumer        consumer name of the calling module
203         * @param       token           token for the authenticated user (e.g. session_id)
204         * @return List of all fields of this study
205         * @return
206         *
207         * If the user is not allowed to read this study, a 401 error is given
208         *
209         * Example REST call (without authentication):
210     * http://localhost:8080/gscf/rest/getStudy/study?studyToken=PPSH
211     *
212         * Returns the JSON object:
213         * {"title":"NuGO PPS human study","studyToken":"PPSH","startDate":"2008-01-13T23:00:00Z",
214         * "Description":"Human study performed at RRI; centres involved: RRI, IFR, TUM, Maastricht U.",
215         * "Objectives":null,"Consortium":null,"Cohort name":null,"Lab id":null,"Institute":null,
216         * "Study protocol":null}
217        */
218        def getStudy = {
219                def items = [:]
220                if( params.studyToken ) {
221                        def study = Study.find( "from Study as s where code=?",[params.studyToken])
222                        if(study) {
223                                // Check whether the person is allowed to read the data of this study
224                                if( !study.canRead(AuthenticationService.getRemotelyLoggedInUser( params.consumer, params.token ))) {
225                                        response.sendError(401)
226                                        return false
227                                }
228                               
229                                study.giveFields().each { field ->
230                                        def name = field.name
231                                        def value = study.getFieldValue( name )
232                                        items[name] = value
233                                }
234                        }
235        }
236                render items as JSON
237        }
238
239
240
241        /**
242         * REST resource for dbNP modules.
243         *
244         * @param       assayToken String, the external identifier of the study
245         * @param       consumer        consumer name of the calling module
246         * @param       token           token for the authenticated user (e.g. session_id)
247         * @return List of all fields of this assay
248         *
249         * Example REST call (without authentication):
250     * http://localhost:8080/gscf/rest/getAssay/assay?assayToken=PPS3_SAM
251     *
252         * Returns the JSON object: {"name":"Lipid profiling","module":{"class":"dbnp.studycapturing.AssayModule","id":1,
253         * "name":"SAM module for clinical data","platform":"clinical measurements","url":"http://sam.nmcdsp.org"},
254         * "assayToken":"PPS3_SAM","parentStudyToken":"PPS","Description":null}
255         */
256        def getAssay = {
257                def items = [:]
258                if( params.assayToken ) {
259                        def assay = Assay.find( "from Assay as a where externalAssayID=?",[params.assayToken])
260                        if(assay) {
261                                assay.giveFields().each { field ->
262                                        def name = field.name
263                                        def value = assay.getFieldValue( name )
264                                        items[name] = value
265                                }
266                                items["parentStudyToken"] = assay.parent.getToken()
267                        }
268        }
269                render items as JSON
270        }
271
272
273
274        /**
275         * REST resource for data modules.
276         * Username and password should be supplied via HTTP Basic Authentication.
277         * One specific sample of a given Assay.
278         *
279         * @param       assayToken      String (id of some Assay in GSCF)
280         * @param       consumer        consumer name of the calling module
281         * @param       token           token for the authenticated user (e.g. session_id)
282         * @return As a JSON object list, for each sample in that assay:
283         * @return 'name' (Sample name, which is unique)
284         * @return 'material' (Sample material)
285         * @return 'subject' (The name of the subject from which the sample was taken)
286         * @return 'event' (the name of the template of the SamplingEvent describing the sampling)
287         * @return 'startTime' (the time the sample was taken relative to the start of the study, as a string)
288         *
289         * Example REST call (without authentication):
290     * http://localhost:8080/gscf/rest/getSample/sam?assayToken=PPS3_SAM&sampleToken=A30_B
291     *
292         * Returns the JSON object:
293         * {"subject":"A30","event":"Liver extraction","startTime":"1 week, 1 hour",
294         * "sampleToken":"A30_B","material":{"class":"dbnp.data.Term","id":6,"accession":"BTO:0000131",
295         * "name":"blood plasma","ontology":{"class":"Ontology","id":2}},"Remarks":null,
296         * "Text on vial":"T70.91709057820039","Sample measured volume":null}
297         */
298        def getSample = {
299                def items = [:]
300                if( params.assayToken && params.sampleToken ) {
301                        def assay = Assay.find( "from Assay as a where externalAssayID=?",[params.assayToken])
302                        if(assay) {
303                                assay.getSamples().each { sample ->
304                                        if( sample.name == params.sampleToken ) {
305                                                        items = [ 
306                                                        'subject'             : sample.parentSubject.name,
307                                                        'event'               : sample.parentEvent.template.name,
308                                                        'startTime'           : sample.parentEvent.getStartTimeString()
309                                                        ]
310                                                        sample.giveFields().each { field ->
311                                                        def name = field.name
312                                                        def value = sample.getFieldValue( name )
313                                                        items[name] = value
314                                }
315                                        }
316                                }
317                        }
318                }
319                render items as JSON
320        }
321
322        /**
323         * Returns the authorization level the user has for a given study.
324         *
325         * If no studyToken is given, a 400 (Bad Request) error is given.
326         * If the given study doesn't exist, a 404 (Not found) error is given.
327         *
328         * @param       consumer        consumer name of the calling module
329         * @param       token           token for the authenticated user (e.g. session_id)
330         * @return      JSON Object
331         * @return  { isOwner: true/false, 'canRead': true/false, 'canWrite': true/false }
332         */
333        def getAuthorizationLevel = {
334                if( params.studyToken ) {
335                        def id = params.studyToken
336                        def study = Study.find( "from Study as s where s.code=?", [id])
337
338                        if( !study ) {
339                                response.sendError(404)
340                                return false
341                        }
342
343                        def user = AuthenticationService.getRemotelyLoggedInUser( params.consumer, params.token );
344                        render( 'isOwner': study.isOwner(user), 'canRead': study.canRead(user), 'canWrite': study.canWrite(user) )
345                } else {
346                        response.sendError(400)
347                        return false
348                }
349    }
350}
Note: See TracBrowser for help on using the repository browser.