Check-in [ab76ada131]
Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Update comments on user.* table. |
---|---|
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
ab76ada131f9c280af44ce2213b5edb5 |
User & Date: | mario 2021-04-06 11:52:30 |
Context
2021-04-06
| ||
18:22 | Rough outline of how to map requests onto fossil commands. check-in: 32f4421035 user: mario tags: trunk | |
11:52 | Update comments on user.* table. check-in: ab76ada131 user: mario tags: trunk | |
2021-04-05
| ||
18:44 | fix PMD extraction regex check-in: f6b70114ca user: mario tags: trunk | |
Changes
Changes to extroot/auth.
︙ | ︙ | |||
79 80 81 82 83 84 85 | } #-- database (== fossil repo) function db($sql="", $params=[]) { static $db; if (empty($db)) { $db = new PDO("sqlite:$_SERVER[FOSSIL_REPOSITORY]"); | | | | 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | } #-- database (== fossil repo) function db($sql="", $params=[]) { static $db; if (empty($db)) { $db = new PDO("sqlite:$_SERVER[FOSSIL_REPOSITORY]"); #$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); } if ($params) { $stmt = $db->prepare($sql); $stmt->execute($params); return $stmt->fetchAll(); } else { return $db->query($sql); } } function create_table() { db("CREATE TABLE IF NOT EXISTS `fx_auth` ( -- IndieAuth token table `code` TEXT, -- OAuth authorization code `type` TEXT, -- one of id,code,token,revoked `scope` TEXT, -- permissions (create,update,delete) `login` TEXT, -- fossil user account `me` TEXT, -- https://userwebid.example.org/ `client_id` TEXT, -- https://remoteapp.example.com/ `redirect_uri` TEXT, -- https://app/login/callback `state` TEXT, -- remote session id (12345..) `code_challenge` TEXT, -- pre-hashed secret for later token req |
︙ | ︙ | |||
129 130 131 132 133 134 135 | print($text); } function h($s) { return htmlspecialchars($s, ENT_QUOTES|ENT_HTML5, "UTF-8"); } | | > > | | | 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 | print($text); } function h($s) { return htmlspecialchars($s, ENT_QUOTES|ENT_HTML5, "UTF-8"); } #-- test if http://identity/ is whitelisted in user.`homepage`/`info` column function allowed_identity($user, $url) { $all = db("SELECT * FROM user WHERE login=?", [$user]); preg_match_all("~\\b https?://(\S+) (?<![,;|<>'\"])~x", join(", ", $all[0]), $uu); # search all fields for urls foreach ($uu[1] as $item) { if (trim_url($item) == trim_url($url)) { return True; } } } function trim_url($url) { # Very crude URL equality test. # Strip any http:// prefix, trailing slashes, or /home, /index (when referring to fossil instance). return strtolower(preg_replace("~ ^https?:// | /+(home|index)$ | /+$ ~x", "", $url)); } function base64_urlencode($raw) { return strtr(trim(base64_encode($raw), "="), "+/", "-_"); } #-- load authorization properties by auth code function get_token_by_code($code) { return db("SELECT * FROM fx_auth WHERE code=?", [$code]) ?: [[]]; } function clean_expired_token() { db("DELETE FROM fx_auth WHERE expires < ?", [time()]); } #-- <form> checkboxes for "scope" token request, extended default list and ?scope=… requests function scope_list($html="") { $scopes = ["create"=>"checked", "update"=>"checked", "delete"=>"", "ticket"=>"checked", "wiki"=>"", "forum"=>""]; if (!empty($_REQUEST["scope"]) and preg_match_all("/(\w+)/", $_REQUEST["scope"], $uu)) { foreach ($uu[1] as $field) { $scopes[$field] = "checked"; } } foreach ($scopes as $field=>$checked) { $html .= "<li style='flex: 1 0 33%; list-style-type: none;'> <label><input type=checkbox $checked name='scope[]' value=$field> $field</label>\n"; |
︙ | ︙ | |||
184 185 186 187 188 189 190 | $code_challenge = $_REQUEST["code_challenge"] ?: ""; $code_challenge_m = $_REQUEST["code_challenge_method"] ?: "S256"; $response_type = $_REQUEST["response_type"] ?: "id"; $h = "h"; # check if $me is allowed if (!allowed_identity($user, $me)) { | | | 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 | $code_challenge = $_REQUEST["code_challenge"] ?: ""; $code_challenge_m = $_REQUEST["code_challenge_method"] ?: "S256"; $response_type = $_REQUEST["response_type"] ?: "id"; $h = "h"; # check if $me is allowed if (!allowed_identity($user, $me)) { return page_html("<h2>Invalid identity</h2> User doesn't have '{$h($me)}' reserved in user table."); } # new code // hashing the properties is a bit overkill, any random id would suffice $code = password_hash("$me/$client_id/$state/$secret", PASSWORD_DEFAULT); db(" INSERT INTO fx_auth (`code`, `type`, `login`, `me`, `client_id`, `redirect_uri`, `state`, `code_challenge`, `code_challenge_m`, `expires`) |
︙ | ︙ | |||
266 267 268 269 270 271 272 273 274 275 276 277 278 279 | elseif (($token["client_id"] != $client_id) or ($token["redirect_uri"] != $redirect_uri)) { die(json_encode(["error" => "invalid_scope", "error_description" => "code does not match previous params (client_id, redirect_uri)"])); } elseif ($code_challenge and base64_urlencode(hash("sha256", $code_verifier, 1)) != $code_challenge) { die(json_encode(["error" => "unauthorized_client", "error_description" => "code_challenge does not match code_verifier"])); } else { die(json_encode(["me" => $token["me"], "scope" => $token["scope"]])); } } #-- run if (empty($_POST["redirect_target"]) and !empty($_REQUEST["code"])) { # ?code=… when the remote app verifies the response | > | 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 | elseif (($token["client_id"] != $client_id) or ($token["redirect_uri"] != $redirect_uri)) { die(json_encode(["error" => "invalid_scope", "error_description" => "code does not match previous params (client_id, redirect_uri)"])); } elseif ($code_challenge and base64_urlencode(hash("sha256", $code_verifier, 1)) != $code_challenge) { die(json_encode(["error" => "unauthorized_client", "error_description" => "code_challenge does not match code_verifier"])); } else { # approved die(json_encode(["me" => $token["me"], "scope" => $token["scope"]])); } } #-- run if (empty($_POST["redirect_target"]) and !empty($_REQUEST["code"])) { # ?code=… when the remote app verifies the response |
︙ | ︙ |