Changeset 2188
- Timestamp:
- Mar 28, 2012, 7:09:01 PM (11 years ago)
- Location:
- trunk/grails-app
- Files:
-
- 3 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/grails-app/controllers/api/ApiController.groovy
r2186 r2188 37 37 38 38 // see if we already have a token on file for this device id 39 def token = Token.findByDeviceID(params .deviceID)40 39 def token = Token.findByDeviceID(params?.deviceID) 40 41 41 // generate a new token if we don't have a token on file 42 42 def result = [:] … … 52 52 ).save(failOnError: true) 53 53 } 54 54 55 55 result = ['token':token.deviceToken,'sequence':token.sequence] 56 56 … … 72 72 } 73 73 74 // @Secured(['ROLE_CLIENT', 'ROLE_ADMIN'])75 74 def getStudies = { 76 75 println "api::getStudies: ${params}" … … 83 82 response.sendError(401, 'Unauthorized') 84 83 } else { 85 // def user = authenticationService.getLoggedInUser() 86 def user = Token.findByDeviceID(deviceID).user 84 def user = Token.findByDeviceID(deviceID)?.user 87 85 def readableStudies = Study.giveReadableStudies(user) 88 86 def studies = [] … … 97 95 'subjects' : study.subjects.size(), 98 96 'species' : study.subjects.species.collect { it.name }.unique(), 99 'assays' : study.assays.collect { it.module.name }.unique(), 97 'assays' : study.assays.collect { it.name }.unique(), 98 'modules' : study.assays.collect { it.module.name }.unique(), 100 99 'events' : study.events.size(), 101 100 'uniqueEvents' : study.events.collect { it.toString() }.unique(), … … 107 106 ] 108 107 } 109 110 108 111 109 def result = [ … … 126 124 } 127 125 128 // @Secured(['ROLE_CLIENT', 'ROLE_ADMIN'])129 126 def getSubjectsForStudy = { 130 127 println "api::getSubjectsForStudy: ${params}" … … 135 132 136 133 // fetch user and study 137 // def user = authenticationService.getLoggedInUser() 138 def user = Token.findByDeviceID(deviceID).user 134 def user = Token.findByDeviceID(deviceID)?.user 139 135 def study = Study.findByStudyUUID(studyToken) 140 136 … … 147 143 response.sendError(401, 'Unauthorized') 148 144 } else { 149 def subjects = [] 150 151 // iterate through subjects 152 study.subjects.each { 153 def fields = it.giveFields() 154 def subject = [:] 155 156 // add subject id 157 subject['id'] = it.id 158 159 // add subject field values 160 fields.each { field -> 161 def value = it.getFieldValue( field.name ) 162 163 if (value.hasProperty('name')) { 164 subject[ field.name ] = value.name 165 } else { 166 subject[ field.name ] = value 167 } 168 } 169 170 subjects[ subjects.size() ] = subject 171 } 172 145 def subjects = apiService.flattenDomainData( study.subjects ) 146 173 147 // define result 174 148 def result = [ 175 'count' : s tudy.subjects.size(),149 'count' : subjects.size(), 176 150 'subjects' : subjects 177 151 ] … … 189 163 } 190 164 191 192 // @Secured(['ROLE_CLIENT', 'ROLE_ADMIN'])193 165 def getAssaysForStudy = { 194 166 println "api::getAssaysForStudy: ${params}" … … 199 171 200 172 // fetch user and study 201 // def user = authenticationService.getLoggedInUser() 202 def user = Token.findByDeviceID(deviceID).user 173 def user = Token.findByDeviceID(deviceID)?.user 203 174 def study = Study.findByStudyUUID(studyToken) 204 175 205 176 // check 206 177 if (!apiService.validateRequest(deviceID,validation)) { 207 println "1"208 178 response.sendError(401, 'Unauthorized') 209 179 } else if (!study) { 210 println "2"211 180 response.sendError(400, 'No such study') 212 181 } else if (!study.canRead(user)) { 213 println "3" 214 response.sendError(401, 'Unauthorized') 215 } else { 182 response.sendError(401, 'Unauthorized') 183 } else { 184 def assays = apiService.flattenDomainData( study.assays ) 185 216 186 // define result 217 187 def result = [ 218 // 'count' : study.subjects.size(),219 // 'subjects' : subjects188 'count' : assays.size(), 189 'assays' : assays 220 190 ] 221 191 -
trunk/grails-app/services/api/ApiService.groovy
r2184 r2188 50 50 } 51 51 } 52 53 def flattenDomainData(elements) { 54 def items = [] 55 56 // iterate through elements 57 elements.each { 58 def fields = it.giveFields() 59 def item = [:] 60 61 // add token 62 if (it.respondsTo('getToken')) { 63 item['token'] = it.getToken() 64 } else { 65 item['id'] = it.id 66 } 67 68 // add subject field values 69 fields.each { field -> 70 def value = it.getFieldValue( field.name ) 71 72 if (value.hasProperty('name')) { 73 item[ field.name ] = value.name 74 } else { 75 item[ field.name ] = value 76 } 77 } 78 79 items[ items.size() ] = item 80 } 81 82 return items 83 } 52 84 } -
trunk/grails-app/views/api/index.gsp
r2186 r2188 2 2 <head> 3 3 <meta name="layout" content="main"/> 4 <style type="text/css"> 5 .api { 6 margin-top: -40px; 7 } 8 9 .api .header { 10 color: #ffda27; 11 font-size: 24px; 12 height: 40px; 13 } 14 15 .api h1 { 16 background-color: #006DBA; 17 padding-left: 10px; 18 margin-top: 40px; 19 height: 30px; 20 padding-top: 10px; 21 color: #fff; 22 text-shadow: 0 1px 2px rgba(0, 0, 0, 0.68); 23 } 24 25 .api h2 { 26 font-size: 12px; 27 background-color: #d7e6f1; 28 padding-left: 10px; 29 margin-top: 10px; 30 height: 20px; 31 padding-top: 5px; 32 font-weight: bold; 33 color: #006DBA; 34 text-shadow: 0 1px 1px rgba(0, 0, 0, 0.28); 35 } 36 37 .api h3 { 38 font-size: 12px; 39 font-weight: bold; 40 color: #ee7624; 41 text-shadow: 0 1px 1px rgba(0, 0, 0, 0.28); 42 } 43 44 .api li { 45 margin-left: 30px; 46 } 47 </style> 4 48 </head> 5 49 <body> 6 <h1>API specification</h1> 50 <div class="api"> 51 <h1 class="header">API specification</h1> 7 52 8 53 The API allows third party software to interface with GSCF and connected modules. 9 54 10 55 <h2>prerequisites</h2> 11 <li>a valid username / password</li> 12 <li>the username should be given the role ROLE_CLIENT</li> 13 <li>a shared secret</li> 56 <li>a valid username / password with role ROLE_CLIENT (see <a href="#authenticate">authenticate</a>)</li> 57 <li>a shared secret (used to calculate the validation md5 hash)</li> 14 58 <li>a deviceID / clientID (look <a href="https://github.com/4np/UIDevice-with-UniqueIdentifier-for-iOS-5" target="_new">here</a> for iOS)</li> 15 59 60 <h2>available API calls</h2> 61 <li><a href="#authenticate">authenticate</a> - set up / synchronize client-server session</li> 62 <li><a href="#getStudies">getStudies</a> - fetch all (readable) studies</li> 63 <li><a href="#getSubjectsForStudy">getSubjectsForStudy</a> - fetch all subjects in a given study</li> 64 <li><a href="#getAssaysForStudy">getAssaysForStudy</a> - fetch all assays in a given study</li> 65 66 <a name="authenticate"></a> 16 67 <h1>authenticate</h1> 68 <h3>url: <g:createLink controller="api" action="authenticate" absolute="true" /></h3> 17 69 <p> 18 70 Authenticate a client using <a href="http://en.wikipedia.org/wiki/Basic_access_authentication" target="_new">HTTP BASIC authentication</a>. 19 After successful authentication, a session token is returned which should be used in all subsequent calls to authorize the API calls. 71 This API call is used to: 72 <li>initially set up a client/server session</li> 73 <li>re-synchronise client/server sessions that become out of sync (e.g. <i>sequence</i> differences)</li> 74 <p> 75 76 <p> 77 After successful authentication, a session token is returned which should the client should store locally. This session token 78 should be used in all subsequent calls to calculate the validation md5 hash. 79 </p> 80 <p> 20 81 This call should also be performed whenever a client/server sessions becomes out of sync (e.g. the client's sequence count 21 differs from the server's sequence count) as the server's sequence count will be returned. For security reasons this api method is 22 designed to be called only once (or when sessions are out of sync) as HTTP BASIC authentication is not really secure (if someone 23 is able to sniff your traffic, the authentication md5 hash is easily stolen).<br/> 24 Every subsequent request the client does, needs to contain a validation MD5 hash, which is a MD5 sum of the concatenation of the device token, 25 the request sequence and a shared secret (e.g. <i>md5sum( token + sequence + shared secret )</i>). 82 differs from the server's sequence count) as the server's sequence count will be returned after successfully authenticating. 83 For security reasons this api method is designed to be called only once (or when sessions are out of sync) as HTTP BASIC authentication 84 is not really secure (if someone is able to sniff your traffic, the authentication md5 hash is easily stolen). API calls are 85 validated using the calculated md5 hash. 86 </p> 87 <p> 88 Every subsequent request the client does, needs to contain the validation MD5 hash, which is a MD5 sum of the concatenation of the device token, 89 the request sequence and a shared secret (e.g. <i>md5sum( token + sequence + shared secret )</i> ).<br/> 26 90 <i>Note that in order to be able to successfully authenticate or use the API in general, the user should have the ROLE_CLIENT assigned!</i> 27 91 … … 40 104 <td>string</td> 41 105 <td>32</td> 42 <td>a unique ID of the client device / application performing the call </td>106 <td>a unique ID of the client device / application performing the call (<a href="https://github.com/4np/UIDevice-with-UniqueIdentifier-for-iOS-5" target="_new">iOS example</a>)</td> 43 107 <td>9ae87836-d38d-4b86-be6a-eff93f2b049a</td> 44 108 <td>yes</td> … … 77 141 </p> 78 142 143 <a name="getStudies"></a> 79 144 <h1>getStudies</h1> 145 <h3>url: <g:createLink controller="api" action="getStudies" absolute="true" /></h3> 80 146 <p> 81 147 Returns the studies which are <i>readable</i> and/or <i>writable</i> for the client. If the client should get access to a particular … … 116 182 </p> 117 183 184 <a name="getSubjectsForStudy"></a> 118 185 <h1>getSubjectsForStudy</h1> 186 <h3>url: <g:createLink controller="api" action="getSubjectsForStudy" absolute="true" /></h3> 119 187 <p> 120 188 Returns the subjects for a particular study … … 161 229 </blockquote> 162 230 </p> 231 232 <a name="getAssaysForStudy"></a> 233 <h1>getAssaysForStudy</h1> 234 <h3>url: <g:createLink controller="api" action="getAssaysForStudy" absolute="true" /></h3> 235 <p> 236 Returns the assays for a particular study 237 238 <h2>Request parameters</h2> 239 <table> 240 <thead> 241 <th>argument</th> 242 <th>type</th> 243 <th>length</th> 244 <th>description</th> 245 <th>example</th> 246 <th>required</th> 247 </thead> 248 <tr> 249 <td>deviceID</td> 250 <td>string</td> 251 <td>36 (max)</td> 252 <td>a unique ID of the client device / application performing the call</td> 253 <td>9ae87836-d38d-4b86-be6a-eff93f2b049a</td> 254 <td>yes</td> 255 </tr> 256 <tr> 257 <td>validation</td> 258 <td>string</td> 259 <td>-</td> 260 <td><a href="http://www.miraclesalad.com/webtools/md5.php" target="_new">md5sum</a>( token + sequence + shared secret )</td> 261 <td>9ae87836d38d4b86be6aeff93f2b049a</td> 262 <td>yes</td> 263 </tr> 264 <tr> 265 <td>studyToken</td> 266 <td>string</td> 267 <td>255</td> 268 <td>study token (see getStudies)</td> 269 <td>b6e0c6f4-d8db-4a43-91fa-a157d2d492f0</td> 270 <td>yes</td> 271 </tr> 272 </table> 273 274 <h2>example reply</h2> 275 <blockquote> 276 {"count":6,"assays":[{"token":"253ec24f-9bac-4f2b-b9cf-f84b86376a4e","name":"16S Sequencing assay","module":"Mass Sequencing module","Description":null},{"token":"4df2f49d-1d8c-48bd-8ebd-d267164948ec","name":"18S Sequencing assay","module":"Mass Sequencing module","Description":null},{"token":"828cf2d6-d797-484b-82f9-df9933d76d77","name":"Glucose assay after","module":"SAM module for clinical data","Description":null},{"token":"d68e8fed-41ca-4408-9d8e-f3598eca9183","name":"Glucose assay before","module":"SAM module for clinical data","Description":null},{"token":"32945764-6c5e-497c-8b1e-0d5e0dfa8221","name":"Lipidomics profile after","module":"Metabolomics module","Description":null,"Spectrometry technique":"GC/MS"},{"token":"92f42f77-1c13-4b25-aa57-b444e355fbf4","name":"Lipidomics profile before","module":"Metabolomics module","Description":null,"Spectrometry technique":"GC/MS"}]} 277 </blockquote> 278 </p> 279 </div> 163 280 </body> 164 281 </html>
Note: See TracChangeset
for help on using the changeset viewer.