root/trunk/grails-app/services/api/ApiService.groovy @ 2217

Revision 2217, 9.0 KB (checked in by work@…, 2 years ago)

fix for hibernate sessions getting mixed up

Line 
1/**
2 * ApiService Service
3 *
4 * Description of my service
5 *
6 * @author  Jeroen Wesbeek <work@osx.eu>
7 * @since       20120328
8 * @package     api
9 *
10 * Revision information:
11 * $Rev: 1430 $
12 * $Author: work@osx.eu $
13 * $Date: 2011-01-21 21:05:36 +0100 (Fri, 21 Jan 2011) $
14 */
15package api
16
17import java.security.MessageDigest
18import dbnp.studycapturing.Assay
19import dbnp.authentication.SecUser
20import grails.converters.JSON
21import org.dbnp.gdt.TemplateEntity
22import org.springframework.context.ApplicationContextAware
23import org.codehaus.groovy.grails.plugins.web.taglib.ApplicationTagLib
24import org.springframework.context.ApplicationContext
25
26class ApiService implements Serializable, ApplicationContextAware {
27    // inject the module communication service
28    def moduleCommunicationService
29
30    // the shared secret used to validate api calls
31    static final String API_SECRET = "th!s_sH0uld^Pr0bab7y_m0v3_t%_th3_uSeR_d0Ma!n_ins7ead!"
32
33    // transactional
34    static transactional = false
35
36    // characters to split on when converting a string to camelCased format
37    static camelCaseSeperators = " |-|_"
38
39    // hasMany keys to ignore when flattening domain data
40    static ignoreHasManyKeys = [
41            "systemFields",
42            "templateBooleanFields",
43            "templateDateFields",
44            "templateDoubleFields",
45            "templateExtendableStringListFields",
46            "templateFileFields",
47            "templateLongFields",
48            "templateModuleFields",
49            "templateTermFields",
50            "templateRelTimeFields",
51            "templateStringFields",
52            "templateStringListFields",
53            "templateTemplateFields",
54            "templateTextFields"
55    ]
56
57    private ApplicationTagLib g
58
59    void setApplicationContext(ApplicationContext applicationContext) {
60        g = applicationContext.getBean(ApplicationTagLib)
61
62        // now you have a reference to g that you can call render() on
63    }
64
65    /**
66     * validate a client request by checking the validation checksum
67     * @param deviceID
68     * @param validation
69     * @return
70     */
71    def validateRequest(String deviceID, String validation) {
72        // disable validation check on development and ci
73        if (['development', 'ci'].contains(grails.util.GrailsUtil.environment)) {
74            return true
75        }
76
77        // get token for this device ID
78        Token token = Token.findByDeviceID(deviceID)
79
80        // increase sequence
81        if (token) {
82            token.sequence = token.sequence+1
83            token.merge(flush: true)
84
85            // generate the validation checksum
86            MessageDigest digest = MessageDigest.getInstance("MD5")
87            String validationSum = new BigInteger(1,digest.digest("${token.deviceToken}${token.sequence}${API_SECRET}".getBytes())).toString(16).padLeft(32,"0")
88
89            // check if the validation confirms
90            return (validation == validationSum)
91        } else {
92            // no such token, re-authenticate
93            return false
94        }
95    }
96
97    /**
98     * flatten domain data to relevant data to return in an api
99     * call and not to expose domain internals
100     *
101     * @param elements (List or Set)
102     * @return
103     */
104    def flattenDomainData(elements) {
105        def items = []
106
107        // iterate through elements
108        elements.each {
109            def fields  = (it.respondsTo('giveFields')) ? it.giveFields(): []
110            def item    = [:]
111
112            // check if element has a name
113            ['name','description'].each { checkName ->
114                if (it.hasProperty(checkName)) item[checkName] = it[checkName]
115            }
116
117            // add token
118            if (it.respondsTo('getToken')) {
119                // some domain methods implement getToken...
120                item['token'] = it.getToken()
121            } else if (it.respondsTo('giveUUID')) {
122                // ...while others implement giveUUID
123                item['token'] = it.giveUUID()
124            } else {
125                // and others don't at all, so far
126                // the consistency...
127                item['id'] = it.id
128            }
129
130            // add subject field values
131            fields.each { field ->
132                // get a camelCased version of the field name
133                def name = field.name.split(camelCaseSeperators).collect {it[0].toUpperCase() + it.substring(1)}.join('')
134                    name = name[0].toLowerCase() + name.substring(1)
135
136                // get the value for this field
137                def value = it.getFieldValue( field.name )
138
139                // add value
140                if (value.hasProperty('name')) {
141                    item[ name ] = value.name
142                } else {
143                    item[ name ] = value
144                }
145            }
146
147            // list hasMany sizes
148            it.properties.hasMany.each { hasManyItem ->
149                if (!ignoreHasManyKeys.contains(hasManyItem.key)) {
150                    // add count for this hasMany item
151                    item[ hasManyItem.key ] = it[ hasManyItem.key ].size()
152                }
153            }
154
155            // add item to resultset
156            items[ items.size() ] = item
157        }
158
159        return items
160    }
161
162    /**
163     * wrapper for performing api calls
164     *
165     * validates if the user may call this api
166     *
167     * @param params
168     * @param response
169     * @param itemName
170     * @param item
171     * @param block
172     */
173    def executeApiCall(params,response,itemName,item,block) {
174        // get variables from parameters
175        String deviceID     = (params.containsKey('deviceID')) ? params.deviceID : ''
176        String validation   = (params.containsKey('validation')) ? params.validation : ''
177
178        // fetch user based on deviceID
179        def user = Token.findByDeviceID(deviceID)?.user
180
181        // check if api call may be performed
182        if (!validateRequest(deviceID,validation)) {
183            // validation md5sum does not match predicted hash
184            response.sendError(401, "Unauthorized")
185        } else if (!item) {
186            // no results, invalid 'item'
187            response.sendError(400, "No such ${itemName}")
188        } else if (item.respondsTo('canRead') && !item.canRead(user)) {
189            // the user cannot read this data
190            response.sendError(401, "Unauthorized")
191        } else if (item.hasProperty('parent') && item.parent.respondsTo('canRead') && !item.parent.canRead(user)) {
192            // the user cannot read this data
193            response.sendError(401, "Unauthorized")
194        } else {
195            // allowed api call, execute block / closure
196            block()
197        }
198    }
199
200    /**
201     * get the measurement tokens from the remote module
202     *
203     * @param assay
204     * @param user
205     * @return
206     */
207    def getMeasurements(Assay assay, SecUser user) {
208        def serviceURL = "${assay.module.url}/rest/getMeasurements"
209        def serviceArguments = "assayToken=${assay.assayUUID}"
210        def json
211
212        // call module method
213        try {
214            json = moduleCommunicationService.callModuleMethod(
215                    assay.module.url,
216                    serviceURL,
217                    serviceArguments,
218                    "POST",
219                    user
220            );
221        } catch (Exception e) {
222            log.error "api.getMeasurements failed :: ${e.getMessage()}"
223            json = new org.codehaus.groovy.grails.web.json.JSONArray()
224        }
225
226        return json
227    }
228
229    /**
230     * get measurement data from the remote module in verbose format
231     *
232     * @param assay
233     * @param user
234     * @return
235     */
236    def getMeasurementData(Assay assay, SecUser user) {
237        def serviceURL = "${assay.module.url}/rest/getMeasurementData"
238        def serviceArguments = "assayToken=${assay.assayUUID}&verbose=true"
239        def json
240
241        // call module method
242        try {
243            json = moduleCommunicationService.callModuleMethod(
244                    assay.module.url,
245                    serviceURL,
246                    serviceArguments,
247                    "POST",
248                    user
249            );
250        } catch (Exception e) {
251            log.error "api.getMeasurementData failed :: ${e.getMessage()}"
252            json = new org.codehaus.groovy.grails.web.json.JSONArray()
253        }
254
255        return json
256    }
257
258    /**
259     * get the measurement meta data from the remote module
260     *
261     * @param assay
262     * @param user
263     * @return
264     */
265    def getMeasurementMetaData(Assay assay, SecUser user) {
266        def serviceURL = "${assay.module.url}/rest/getMeasurementMetaData"
267        def serviceArguments = "assayToken=${assay.assayUUID}"
268        def json
269
270        // call module method
271        try {
272            json = moduleCommunicationService.callModuleMethod(
273                    assay.module.url,
274                    serviceURL,
275                    serviceArguments,
276                    "POST",
277                    user
278            );
279        } catch (Exception e) {
280            log.error "api.getMeasurementMetaData failed :: ${e.getMessage()}"
281            json = new org.codehaus.groovy.grails.web.json.JSONArray()
282        }
283
284        return json
285    }
286}
Note: See TracBrowser for help on using the browser.