Collection of themes/skins for the Fossil SCM

⌈⌋ branch:  Fossil Skins Extra


Check-in [392da55840]

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Prepare (pre-registered) autologin feature, and auto-create table on connection.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 392da55840a9a2a02f8766be81427f4d330324a1
User & Date: mario 2021-04-08 05:08:14
Context
2021-04-08
05:08
Restructure to use global type $map. check-in: 9d031a285f user: mario tags: trunk
05:08
Prepare (pre-registered) autologin feature, and auto-create table on connection. check-in: 392da55840 user: mario tags: trunk
2021-04-07
15:49
Working version for technote submissions, except that Location: header doesn't pass through fossil. Support HTTP_COOKIE as alias for _AUTHORIZATION. Remove proc_open for plain exec and echo| pipe. check-in: 7bc953e13d user: mario tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to extroot/auth.

1
2
3
4
5

6
7
8
9
10
11
12
13
14
15
16
..
80
81
82
83
84
85
86

87
88
89
90
91
92
93
...
130
131
132
133
134
135
136
137



138
139
140









141
142
143
144
145
146
147
...
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208








209
210
211
212
213
214
215
...
281
282
283
284
285
286
287

288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
#!/usr/bin/php-cgi -dcgi.force_redirect=0
<?php
# encoding: utf-8
# api: cgi
# type: auth

# title: IndieAuth authorization_endpoint
# description: Verifies a remote user id (URL) against active fossil login
# version: 0.5
# state: alpha
# depends: php:curl, php:sqlite
# doc: https://www.w3.org/TR/indieauth/#authentication,
#     https://indieweb.org/authorization-endpoint
# config: -
#
# Minimal implementation of IndieAuth login endpoint. Runs as fossil cgi
# extension to verify currently logged in user. Confirms IndieAuth/OAuth
................................................................................

#-- 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 {
................................................................................
}
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.
................................................................................
    $state = $_REQUEST["state"] ?: "";
    $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`)
        VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
        [$code, $response_type, $user, $me, $client_id, $redirect_uri, $state, $code_challenge, $code_challenge_m, time()+300]
    );

    # construct confirmation+redirect url
    $url = $redirect_uri
         . (strstr($redirect_uri, "?") ? "&" : "?")
         . "code=" . urlencode($code) . "&state=" . urlencode($state);








    
    # output page
    if ($response_type == "code") {
        $scope = scope_list();
    }
    $html = <<<HTML

................................................................................
#-- run
if (empty($_POST["redirect_target"]) and !empty($_REQUEST["code"])) {  # ?code=… when the remote app verifies the response
    verify_code();
}
elseif (empty($_SERVER["FOSSIL_USER"])) {   # user must be signed in at this point
    page_html("<h2>Not logged in</h2>\n Request can't be authorized, unless you're <a href='../login'>logged in</a>.");
}

elseif (!empty($_REQUEST["me"])) {   # ?me=… starts an authorization request
    process_request();
}
elseif (!empty($_POST["confirm"])) {   # ?redirect_target=…  for confirmation button
    confirm();
}
else {
    create_table();
    page_html("
       <h3>Authorization endpoint</h3>
       There was no ?code= or ?me= parameter,<br> so not an actual Indie/OAuth request.
       <ul>
         <li>The <code>fx_auth</code> table is now configured.
         <li>Users still need to register a web address in the info/homepage field. (See <a href='user_config'>ext/user_config</a>.)
         <li>And then declare this endpoint on their personal homepage:<br>




|
>


|
|







 







>







 







|
>
>
>
|
<
<
>
>
>
>
>
>
>
>
>







 







|













|
|
|
>
>
>
>
>
>
>
>







 







>







<







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
..
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
...
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
...
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
...
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315

316
317
318
319
320
321
322
#!/usr/bin/php-cgi -dcgi.force_redirect=0
<?php
# encoding: utf-8
# api: cgi
# type: form
# category: auth
# title: IndieAuth authorization_endpoint
# description: Verifies a remote user id (URL) against active fossil login
# version: 0.6
# state: beta
# depends: php:curl, php:sqlite
# doc: https://www.w3.org/TR/indieauth/#authentication,
#     https://indieweb.org/authorization-endpoint
# config: -
#
# Minimal implementation of IndieAuth login endpoint. Runs as fossil cgi
# extension to verify currently logged in user. Confirms IndieAuth/OAuth
................................................................................

#-- 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);
        create_table();
    }
    if ($params) {
        $stmt = $db->prepare($sql);
        $stmt->execute($params);
        return $stmt->fetchAll();
    }
    else {
................................................................................
}
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, &$may_autologin) {
    # search all fields for urls
    preg_match_all(
         "~\\b https?://(\S+) (?<![,;|<>'\"])~x",
         join(", ", db("SELECT * FROM user WHERE login=?", [$user])[0]),


         $allowed
    );
    $allowed = $allowed[1];
    # if list contains "http://autologin/
    if (in_array("autologin", $allowed)) {
        $may_autologin = $allowed; # should contain callback urls
    }
    # but verify $me being whitelisted first
    foreach ($allowed as $item) {
        if (trim_url($item) == trim_url($url)) {
             return True;
        }
    }
}
function trim_url($url) {
    # Very crude URL equality test.
................................................................................
    $state = $_REQUEST["state"] ?: "";
    $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, $may_autologin)) {
        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`)
        VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
        [$code, $response_type, $user, $me, $client_id, $redirect_uri, $state, $code_challenge, $code_challenge_m, time()+300]
    );

    # construct confirmation+redirect url
    $url= $redirect_uri
        . (strstr($redirect_uri, "?") ? "&" : "?")
        . "code=" . urlencode($code) . "&state=" . urlencode($state);
    
    # autologin (meant for upstream ticket bridges, requires both identity and callback urls to be whitelisted)
    if ($may_autologin) {
        if (in_array(trim_url($redirect_uri), $may_autologin)) {
            file_get_contents($url);
            die(header("Location: $url"));
        }
    }
    
    # output page
    if ($response_type == "code") {
        $scope = scope_list();
    }
    $html = <<<HTML

................................................................................
#-- run
if (empty($_POST["redirect_target"]) and !empty($_REQUEST["code"])) {  # ?code=… when the remote app verifies the response
    verify_code();
}
elseif (empty($_SERVER["FOSSIL_USER"])) {   # user must be signed in at this point
    page_html("<h2>Not logged in</h2>\n Request can't be authorized, unless you're <a href='../login'>logged in</a>.");
}
https://fossil.include-once.org/fossil-skins/ext/auth?client_id=https%3A%2F%2Ftokens-pls.herokuapp.com&code_challenge=5l8nYg8xcgwcEg2TN0SWeSJgjOejWZqu2CvtBnNjgcE&code_challenge_method=S256&nonce=4f4f480199de98745a0e6cebda898a34&redirect_uri=https%3A%2F%2Ftokens-pls.herokuapp.com%2Fcallback&response_type=code&scope=draft&state=aa823b9cef0ae758b6bf61a67dd464ca987770338eb7e1160d945c874efdf3b8
elseif (!empty($_REQUEST["me"])) {   # ?me=… starts an authorization request
    process_request();
}
elseif (!empty($_POST["confirm"])) {   # ?redirect_target=…  for confirmation button
    confirm();
}
else {

    page_html("
       <h3>Authorization endpoint</h3>
       There was no ?code= or ?me= parameter,<br> so not an actual Indie/OAuth request.
       <ul>
         <li>The <code>fx_auth</code> table is now configured.
         <li>Users still need to register a web address in the info/homepage field. (See <a href='user_config'>ext/user_config</a>.)
         <li>And then declare this endpoint on their personal homepage:<br>