1 # ===================================================================== 2 # authlib.rc: application-level authentication functions for the Web. 3 # 4 # Copyright (c) 2003-2014 Carlo Strozzi 5 # 6 # This program is free software; you can redistribute it and/or modify 7 # it under the terms of the GNU General Public License as published by 8 # the Free Software Foundation; version 2 dated June, 1991. 9 # 10 # This program is distributed in the hope that it will be useful, 11 # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 # GNU General Public License for more details. 14 # 15 # You should have received a copy of the GNU General Public License 16 # along with this program; if not, write to the Free Software 17 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 18 # 19 # ===================================================================== 20 21 # ===================================================================== 22 # Application-level authentication functions (note how they are given 23 # names that belong to the application-space). 24 # ===================================================================== 25 26 # ===================================================================== 27 # authClear [ssf] 28 # 29 # This function is currently used only by authCheck.slave, see below. 30 # ===================================================================== 31 32 fn authClear { 33 34 ~ $CSA_DEBUG 0 || csaDebug $0 $* 35 36 csaSession.set - 1 37 csaSession.set - 2 38 csaSession.set - 9 39 csaSession.set - 19 40 41 HTTP_AUTHORIZATION = 'Basic Cg==' 42 43 ~ $1 tag:* && csaTrapFile $TMPDIR/$1-ssf 44 } 45 46 # ===================================================================== 47 # authCheck.slave [-q|--quiet] [-r|--return] [ssf] 48 # 49 # Special version of authCheck, to be used by "slave" CSA weblets. 50 # If no 'ssf' argument is passed then it will default to the relevant 51 # element in the current session, if any. If '-q' is specified, 52 # the printing of message 0013 is suppressed. Sets CSA_AUTH_OK on 53 # return. It is up to the CSA AWK front-end to ensure that all 54 # authentication variables are set to proper values and contain only 55 # allowed characters. If '-r' is specified, the function returns to the 56 # caller in case of errors, while the normal behaviour is be to exit 57 # with an error message. 58 # 59 # Note that this function is likely to be called at the beginning 60 # of each Web application script, so it is the most suitable place 61 # to do accounting where necessary, as in fee-based ASP services, etc. 62 # ===================================================================== 63 64 fn authCheck.slave { 65 66 ~ $CSA_DEBUG 0 || csaDebug $0 $* 67 68 tns1=() tns2=() tns3=() { 69 70 while (!~ $1 ()) { 71 switch ($1) { 72 case -q --quiet 73 tns1 = 1 74 case -r --return 75 tns2 = 1 76 case tag:* 77 tns3 = $1 78 } 79 shift 80 } 81 82 # Try and load the user ID from the secondary session file. Note 83 # that CSA slave weblets can *only* receive CSA_AUTH_USER through 84 # a secondary session file created by the main application, *even 85 # in the case that server-managed HTTP basc auth is also being 86 # used*. That's why a CSA slave application needs to have 87 # 'CSA_AUTH_TYPE=custom' set in 'weblib.rc', regardless of any 88 # different values (such as 'server' or 'basic') that may have been 89 # set by the CSA CGI libraries or in the CSA application profiles. 90 # Therefore, if the expected SSF does not exist then the session is 91 # immediately invalidated. 92 93 # Unless this was set to something different by the caller, its 94 # value is fetched from the current session. 95 ~ $tns3 *[a-z]* || tns3 = $CSA_SESSION(19) 96 97 ~ $tns3 () && { 98 authClear 99 ~ $tns1 () || csaPrintMsg 0013 100 ~ $tns2 () || return 1 101 csaExit.needauth $CSA_AUTH_REALM 102 } 103 104 # This method of loading the caller's session data only works 105 # with slave CSA weblets that run on the same machine as the 106 # master application. If that's not the case then the slave needs 107 # to get the required pieces of info by calling the master's 108 # getSession method over HTTP instead. 109 110 * = ``('|'){cat $TMPDIR/$tns3-ssf} 111 CSA_AUTH_USER=$1; TNS_AUTH_GRP=$2 112 113 # Since the SSF may contain additional info, let's use them to 114 # set other user-related variables in a way similar to what is 115 # normally done by a master CSA web service authCheck function. 116 117 tbl_user.k_user = $CSA_AUTH_USER 118 tbl_user.u_group = $TNS_AUTH_GRP 119 tbl_user.u_email = $3 120 tbl_user.u_name = $4 121 tbl_user.u_sname = $5 122 123 if (~ $CSA_AUTH_USER ()) { 124 authClear 125 ~ $tns1 () || csaPrintMsg 0013 126 ~ $tns2 () || return 1 127 csaExit.needauth $CSA_AUTH_REALM 128 } else { 129 csaSession.set $CSA_AUTH_USER 1 130 csaSession.set $TNS_AUTH_GRP 9 131 csaSession.set $tns3 19 132 CSA_AUTH_OK = 1 133 CSA_STATUS = 0 134 return 0 # session granted. 135 } 136 } 137 } 138 139 # ===================================================================== 140 # authCheck [-q|--quiet] [-o|--open-id] [-r|--return] 141 # 142 # Check that the client provided the correct authorization credentials. 143 # If '-q' is specified, the printing of message 0013 is suppressed. 144 # Sets CSA_AUTH_OK on return. It is up to the CSA AWK front-end to 145 # ensure that all authentication variables are set to proper values and 146 # contain only allowed characters. If '-r' is specified, the function 147 # returns to the caller in case of errors, while the normal behaviour 148 # would be to exit with an error message. If '-o' is specified, then 149 # the client is expected to have already authenticated with an either 150 # an RPX or an OpenID provider. If server-managed HTTP authentication 151 # is in use it will override any other authentication types, but most 152 # web servers will not expose it on the CGI interface, although the CSA 153 # CGI front-end will still be able to set CSA_AUTH_USER. If CSA-managed 154 # HTTP basci authentication is in use, it will override all other 155 # authentication types except the server-managed one, if any. This 156 # function will also CSA_AUTH_PW from the user table, albeit in case of 157 # server-managed auth the latter should be empty. 158 # 159 # Note that this function is likely to be called at the beginning 160 # of each Web application script, so it is the most suitable place 161 # to do accounting where necessary, as in fee-based ASP services, etc. 162 # ===================================================================== 163 164 fn authCheck { 165 166 ~ $CSA_DEBUG 0 || csaDebug $0 $* 167 168 # Set default. 169 ~ $TNS_USER_TABLE () && TNS_USER_TABLE = $CSA_ROOT/var/pages/auth+dat 170 171 # At this stage this should contain something, but providing 172 # a default value won't hurt anyway. 173 174 ~ $TNS_GROUP_META(1) () && TNS_GROUP_META = TypeWriter 175 176 tns1=() tns2=() tns3=() tns4=() tns5=() user_auth=() { 177 178 while (!~ $1 ()) { 179 switch ($1) { 180 case -o --open-id 181 ~ $CSA_SESSION(15) http://* https://* && tns5 = 1 182 case -q --quiet 183 tns1 = 1 184 case -r --return 185 tns2 = 1 186 } 187 shift 188 } 189 190 # I will conceed that if a user is already logged in and she 191 # is referring to a valid session file, then she may stay 192 # logged-in without checking her credentials again, even if she 193 # is using either OpenID or CSA-managed HTTP basic auth. On the 194 # other hand, if CSA_SESSION(1) is not null but CSA_AUTH_USER is 195 # null (which implies that REMOTE_USER is null too), then the 196 # session is considered to be invalid and authentication fails. 197 # 198 # If any time-based session expiration logic is needed it will 199 # have to be applied here. If we really want to drop-out an 200 # already authenticated user we will have to remove the relevant 201 # session file from the server. 202 203 if (!~ $CSA_AUTH_USER () && ~ $CSA_AUTH_USER $CSA_SESSION(1)) { 204 if (!~ $CSA_SESSION(9) () && !~ $CSA_SESSION(9) -) { 205 TNS_AUTH_GRP = $CSA_SESSION(9) 206 207 # Carry old values forward into the next session file, 208 # also to prevent triggering the actual user table 209 # lookup every second request. 210 211 csaSession.set $CSA_SESSION(1) 1 212 csaSession.set $CSA_SESSION(9) 9 213 csaSession.set $CSA_SESSION(17) 17 214 csaSession.set $CSA_SESSION(21) 21 215 216 # This is needed for gravatar support. 217 tpl.var.tw.user.email.md5 = $CSA_SESSION(21) 218 219 # This also needs to be carried forward, both to 220 # abide by the TW specs and for proper housekeeping 221 # upon log-out. 222 223 csaSession.set $CSA_SESSION(19) 19 224 225 CSA_AUTH_OK = 1 226 CSA_STATUS = 0 227 return 0 # session granted. 228 } 229 } else { 230 csaSession.set - 1 231 csaSession.set - 9 232 csaSession.set - 17 233 #csaSession.set - 19 234 } 235 236 csaAssign CSA_PBC - 2 # needs to be re-verified 237 238 # This variable may not be null, the password can. 239 240 if (~ $CSA_AUTH_USER ()) { 241 ~ $tns1 () || csaPrintMsg 0013 242 243 ~ $tns2 () || return 1 244 245 csaExit.needauth $CSA_AUTH_REALM # Authorization failed. 246 } 247 248 # Read the relevant user record for full authentication. 249 # This is necessary also in case of both server- and CSA- 250 # managed HTTP auth, because the user table contains more 251 # data than what is provided by either methods. 252 253 csaMkTemp tns4 254 csa-tbl2rc --input $TNS_USER_TABLE \ 255 --key $CSA_AUTH_USER --prefix tbl_user. > $tns4 || 256 csaExit.fault 0003 csa-tbl2rc 257 . $tns4 258 259 if (~ $'tbl_user.k_user' ()) { 260 # Handle invalid user. 261 tbl_user.u_passwd = () # hide password in env. 262 ~ $tns2 () || return 1 263 264 csaExit.needauth $CSA_AUTH_REALM # Authorization failed. 265 } 266 267 # Do not require a password if the client has already 268 # successfully authenticated with either OpenID or 269 # server-managed authentication. 270 271 if (!~ $tns5 () || !~ $REMOTE_USER ()) { 272 273 CSA_AUTH_PW = $'tbl_user.u_passwd' 274 275 } else { 276 277 # Handle invalid password (may not occur with OpenID/Server- 278 # managed auth, of course). 279 280 if (!~ $'tbl_user.u_passwd' $CSA_AUTH_PW) { 281 tbl_user.u_passwd = () # hide password in env. 282 ~ $tns2 () || return 1 283 csaExit.needauth $CSA_AUTH_REALM # Authorization failed. 284 } 285 286 tbl_user.u_passwd = () # hide password in env. 287 } 288 289 # Deny access to suspended/disabled accounts. 290 if (~ $'tbl_user.u_gui' -* || ~ $'tbl_user.u_email' x) { 291 csaExit.needauth $CSA_AUTH_REALM 292 } 293 294 TNS_AUTH_GRP = $'tbl_user.u_group' 295 tbl_user.u_passwd = () # hide password in env. 296 297 # Add leading/trailing comma if necessary. 298 if (~ $TNS_AUTH_GRP *[a-z0-9]*) { 299 ~ $TNS_AUTH_GRP ,* || TNS_AUTH_GRP = ,$TNS_AUTH_GRP 300 ~ $TNS_AUTH_GRP *, || TNS_AUTH_GRP = $TNS_AUTH_GRP, 301 302 # Any authenticated client is also a 'user'. 303 ~ ,$TNS_AUTH_GRP, *,user,* || TNS_AUTH_GRP = $TNS_AUTH_GRP^user, 304 305 csaSession.set $TNS_AUTH_GRP 9 306 } else TNS_AUTH_GRP = () # clear if invalid. 307 308 # Everything ok, session granted. 309 310 * = () { 311 * = `{csa-keypath $CSA_AUTH_USER 1,1/2,1} 312 ~ $1 */* && !~ $1 *^'?' && csaAssign CSA_PBC $1 2 313 } 314 315 csaSession.set $CSA_AUTH_USER 1 316 csaSession.set $CSA_TIME_ISO 2 317 318 # Create also the secondary session file. For more flexibilty, 319 # this will contain only those minimal pieces of information 320 # that do not change across the application workflow, such as the 321 # user-ID, the authentication groups the user belongs to, and so on. 322 # Each piece of information will be stored in a separate line, 323 # which means that it may not contain embedded newlines. While 324 # the name of the main sesion file changes upon each client 325 # request, the one of the SSF is meant to be persistent. 326 # Slave weblets receive the SSF name in an invocation parameter 327 # and are supposed to enquiry the master application via HTTP 328 # to read the actual SSF contents. In those special cases where 329 # a slave weblet runs on the same server of the master application, 330 # the received SSF can also be accessed directly through the 331 # filesystem. 332 333 echo $CSA_AUTH_USER'|'$TNS_AUTH_GRP'|'$'tbl_user.u_email'^'|'$'tbl_user.u_name'^'|'$'tbl_user.u_sname' > $TMPDIR/$CSA_SESSION(19)^-ssf 334 335 CSA_AUTH_OK = 1 336 337 # Set preferred editor GUI for this user, if defined and known. 338 339 switch ($'tbl_user.u_gui') { 340 case tinymce nicedit ckeditor parsewiki rawhtml 341 csaSession.set $'tbl_user.u_gui' 11 342 343 case default # backward-compatibility 344 csaSession.set tinymce 11 345 346 # add more cases here as needed. 347 348 case () # default is no GUI 349 csaSession.set - 11 350 351 case * # default is no GUI 352 csaSession.set - 11 353 } 354 355 csaSession.set $'tbl_user.u_email' 13 356 357 # Support for gravatars. This must be in MD5 format so I 358 # cannot rely on CSA_CMD_MD, because this could be anything. 359 * = `{echo -n $'tbl_user.u_email' | md5sum} 360 ~ $1 () && csaExit.fault 0003 md5sum # just in case. 361 csaSession.set $1 21 362 tpl.var.tw.user.email.md5 = $1 363 364 # Set user's display name. 365 csaSession.set $CSA_AUTH_USER 17 immediate # default first. 366 if (!~ $'tbl_user.u_name' () || !~ $'tbl_user.u_sname' ()) { 367 if (!~ $'tbl_user.u_name' ()) { 368 csaSession.set \ 369 $'tbl_user.u_name'^' '$'tbl_user.u_sname' 17 immediate 370 } else csaSession.set $'tbl_user.u_sname' 17 immediate 371 } 372 373 # This is why we need "csaSession.set ... immediate" above. 374 TNS_AUTH_DISPLAY = $CSA_SESSION(17) 375 376 # Set default TW authorship, if available. 377 ~ $'tbl_user.u_name' () || tns3 = $'tbl_user.u_name' 378 379 if (!~ $'tbl_user.u_sname' ()) { 380 ~ $tns3 () || tns3 = $tns3^' ' 381 tns3 = $tns3$'tbl_user.u_sname' 382 } 383 384 if (!~ $tns3 ()) { 385 CONTENT_LENGTH=100 { 386 tns3 = `{echo -n $tns3 | csa-escape -u} 387 } 388 } 389 390 ~ $tns3 () || csaCookie.set twauthor $tns3 \ 391 --expires ``($nl){date -u -d now+6month +'%a, %d-%b-%Y %H:%M:%S GMT'} 392 } 393 394 CSA_STATUS=0 395 } 396 397 # ===================================================================== 398 # End of authlib.rc 399 # =====================================================================