| rev | line source | 
| bsw/jbe@1309 | 1 local function show_error(text) | 
| bsw/jbe@1309 | 2   ui.title("Authorization") | 
| bsw/jbe@1309 | 3   ui.section(function() | 
| bsw/jbe@1309 | 4     ui.sectionHead(function() | 
| bsw/jbe@1309 | 5       ui.heading{ content = _"Error during authorization" } | 
| bsw/jbe@1309 | 6     end) | 
| bsw/jbe@1309 | 7     ui.sectionRow(function() | 
| bsw/jbe@1309 | 8       ui.container{ content = text } | 
| bsw/jbe@1309 | 9     end ) | 
| bsw/jbe@1309 | 10   end ) | 
| bsw/jbe@1309 | 11 end | 
| bsw/jbe@1309 | 12 | 
| bsw/jbe@1309 | 13 local client_id = param.get("client_id") | 
| bsw/jbe@1309 | 14 local redirect_uri = param.get("redirect_uri") | 
| bsw/jbe@1309 | 15 local redirect_uri_explicit = redirect_uri and true or false | 
| bsw/jbe@1309 | 16 local response_type = param.get("response_type") | 
| bsw/jbe@1309 | 17 local state = param.get("state") | 
| bsw/jbe@1309 | 18 | 
| bsw/jbe@1309 | 19 local no_scope_requested = true | 
| bsw/jbe@1309 | 20 | 
| bsw/jbe@1309 | 21 local scopes = { | 
| bsw/jbe@1309 | 22   [0] = param.get("scope") | 
| bsw/jbe@1309 | 23 } | 
| bsw/jbe@1309 | 24 | 
| bsw/jbe@1309 | 25 for i = 1, math.huge do | 
| bsw/jbe@1309 | 26   scopes[i] = param.get("scope" .. i) | 
| bsw/jbe@1309 | 27   if not scopes[i] then | 
| bsw/jbe@1309 | 28     break | 
| bsw/jbe@1309 | 29   end | 
| bsw/jbe@1309 | 30 end | 
| bsw/jbe@1309 | 31 | 
| bsw/jbe@1309 | 32 if #scopes == 0 and not scopes[0] then | 
| bsw/jbe@1309 | 33   scopes[0] = "identification" | 
| bsw/jbe@1309 | 34 end | 
| bsw/jbe@1309 | 35 | 
| bsw/jbe@1309 | 36 local requested_scopes = {} | 
| bsw/jbe@1309 | 37 | 
| bsw/jbe@1309 | 38 for i = 0, #scopes do | 
| bsw/jbe@1309 | 39   if scopes[i] then | 
| bsw/jbe@1309 | 40     for scope in string.gmatch(scopes[i], "[^ ]+") do | 
| bsw/jbe@1309 | 41       requested_scopes[scope] = true | 
| bsw/jbe@1309 | 42     end | 
| bsw/jbe@1309 | 43   end | 
| bsw/jbe@1309 | 44 end | 
| bsw/jbe@1309 | 45 | 
| bsw/jbe@1309 | 46 local system_application | 
| bsw/jbe@1309 | 47 local member_application | 
| bsw/jbe@1309 | 48 local client_name | 
| bsw/jbe@1309 | 49 local scopes_to_accept = table.new(requested_scopes) | 
| bsw/jbe@1309 | 50 local accepted_scopes = {} | 
| bsw/jbe@1309 | 51 | 
| bsw/jbe@1309 | 52 local domain | 
| bsw/jbe@1309 | 53 | 
| bsw/jbe@1309 | 54 if client_id then | 
| bsw/jbe@1309 | 55   domain = string.match(client_id, "^dynamic:([a-z0-9.-]+)$") | 
| bsw/jbe@1309 | 56 end | 
| bsw/jbe@1309 | 57 | 
| bsw/jbe@1309 | 58 local dynamic_application_check | 
| bsw/jbe@1309 | 59 if domain then | 
| bsw/jbe@1309 | 60   if #domain > 255 then | 
| bsw/jbe@1309 | 61     return show_error(_"Domain too long") | 
| bsw/jbe@1309 | 62   end | 
| bsw/jbe@1309 | 63   if string.find(domain, "^%.") or string.find(domain, "%.$") or string.find(domain, "%.%.") then | 
| bsw/jbe@1309 | 64     return show_error(_"Invalid domain format") | 
| bsw/jbe@1309 | 65   end | 
| bsw/jbe@1309 | 66   if redirect_uri then | 
| bsw/jbe@1309 | 67     local redirect_uri_domain, magic = string.match(redirect_uri, "^[Hh][Tt][Tt][Pp][Ss]://([A-Za-z0-9_.-]+)/(.*)$") | 
| bsw/jbe@1309 | 68     if not redirect_uri_domain or string.lower(redirect_uri_domain) ~= domain or magic ~= config.oauth2.endpoint_magic then | 
| bsw/jbe@1309 | 69       return show_error(_"Redirect URI forbidden") | 
| bsw/jbe@1309 | 70     end | 
| bsw/jbe@1309 | 71   else | 
| bsw/jbe@1309 | 72     redirect_uri = "https://" .. domain .. "/" .. config.oauth2.endpoint_magic | 
| bsw/jbe@1309 | 73   end | 
| bsw/jbe@1309 | 74   dynamic_application_check = DynamicApplicationScope:check_scopes(domain, redirect_uri, response_type, requested_scopes) | 
| bsw/jbe@1309 | 75   if dynamic_application_check == "not_registered" then | 
| bsw/jbe@1309 | 76     return show_error(_"Redirect URI or response type not registered") | 
| bsw/jbe@1309 | 77   end | 
| bsw/jbe@1309 | 78   client_name = domain | 
| bsw/jbe@1309 | 79   member_application = MemberApplication:by_member_id_and_domain(app.session.member_id, domain) | 
| bsw/jbe@1309 | 80   if member_application then | 
| bsw/jbe@1309 | 81     for scope in string.gmatch(member_application.scope, "[^ ]+") do | 
| bsw/jbe@1309 | 82       accepted_scopes[scope] = true | 
| bsw/jbe@1309 | 83       scopes_to_accept[scope] = nil | 
| bsw/jbe@1309 | 84     end | 
| bsw/jbe@1309 | 85   end | 
| bsw/jbe@1309 | 86 else | 
| bsw/jbe@1309 | 87   system_application = SystemApplication:by_client_id(client_id) | 
| bsw/jbe@1309 | 88   if system_application then | 
| bsw/jbe@1309 | 89     if redirect_uri_explicit then | 
| bsw/jbe@1309 | 90       if | 
| bsw/jbe@1309 | 91         redirect_uri ~= system_application.default_redirect_uri | 
| bsw/jbe@1309 | 92         and not SystemApplicationRedirectUri:by_pk(system_application.id, redirect_uri) | 
| bsw/jbe@1309 | 93       then | 
| bsw/jbe@1309 | 94         return show_error(_"Redirect URI invalid") | 
| bsw/jbe@1309 | 95       end | 
| bsw/jbe@1309 | 96     else | 
| bsw/jbe@1309 | 97       redirect_uri = system_application.default_redirect_uri | 
| bsw/jbe@1309 | 98     end | 
| bsw/jbe@1309 | 99     if system_application.flow ~= response_type then | 
| bsw/jbe@1309 | 100       return show_error(_"Response type not allowed for given client") | 
| bsw/jbe@1309 | 101     end | 
| bsw/jbe@1309 | 102     client_name = system_application.name | 
| bsw/jbe@1309 | 103     member_application = MemberApplication:by_member_id_and_system_application_id(app.session.member_id, system_application.id) | 
| bsw/jbe@1309 | 104   end | 
| bsw/jbe@1309 | 105 end | 
| bsw/jbe@1309 | 106 | 
| bsw/jbe@1309 | 107 if not client_name then | 
| bsw/jbe@1309 | 108   return show_error(_"Client ID invalid") | 
| bsw/jbe@1309 | 109 end | 
| bsw/jbe@1309 | 110 | 
| bsw/jbe@1309 | 111 local function error_redirect(error_code, description) | 
| bsw/jbe@1309 | 112   local params = { | 
| bsw/jbe@1309 | 113     state = state, | 
| bsw/jbe@1309 | 114     error = error_code, | 
| bsw/jbe@1309 | 115     error_description = description | 
| bsw/jbe@1309 | 116   } | 
| bsw/jbe@1309 | 117   if response_type == "token" then | 
| bsw/jbe@1309 | 118     local anchor_params_list = {} | 
| bsw/jbe@1309 | 119     for k, v in pairs(params) do | 
| bsw/jbe@1309 | 120       anchor_params_list[#anchor_params_list+1] = k .. "=" .. encode.url_part(v) | 
| bsw/jbe@1309 | 121     end | 
| bsw/jbe@1309 | 122     local anchor = table.concat(anchor_params_list, "&") | 
| bsw/jbe@1309 | 123     request.redirect{ | 
| bsw/jbe@1309 | 124       external = redirect_uri .. "#" .. anchor | 
| bsw/jbe@1309 | 125     } | 
| bsw/jbe@1309 | 126   else | 
| bsw/jbe@1309 | 127     request.redirect{ | 
| bsw/jbe@1309 | 128       external = redirect_uri, | 
| bsw/jbe@1309 | 129       params = params | 
| bsw/jbe@1309 | 130     } | 
| bsw/jbe@1309 | 131   end | 
| bsw/jbe@1309 | 132 end | 
| bsw/jbe@1309 | 133 | 
| bsw/jbe@1309 | 134 if response_type ~= "code" and response_type ~= "token" then | 
| bsw/jbe@1309 | 135   return error_redirect("unsupported_response_type", "Invalid response type") | 
| bsw/jbe@1309 | 136 end | 
| bsw/jbe@1309 | 137 | 
| bsw/jbe@1309 | 138 for i = 0, #scopes do | 
| bsw/jbe@1309 | 139   if scopes[i] == "" then | 
| bsw/jbe@1309 | 140     return error_redirect("invalid_scope", "Empty scope requested") | 
| bsw/jbe@1309 | 141   end | 
| bsw/jbe@1309 | 142 end | 
| bsw/jbe@1309 | 143 | 
| bsw/jbe@1309 | 144 for scope in pairs(requested_scopes) do | 
| bsw/jbe@1309 | 145   local scope_valid = false | 
| bsw/jbe@1309 | 146   for i, entry in ipairs(config.oauth2.available_scopes) do | 
| bsw/jbe@1309 | 147     if scope == entry.scope or scope == entry.scope .. "_detached" then | 
| bsw/jbe@1309 | 148       scope_valid = true | 
| bsw/jbe@1309 | 149       break | 
| bsw/jbe@1309 | 150     end | 
| bsw/jbe@1309 | 151   end | 
| bsw/jbe@1309 | 152   if not scope_valid then | 
| bsw/jbe@1309 | 153     return error_redirect("invalid_scope", "Requested scope not available") | 
| bsw/jbe@1309 | 154   end | 
| bsw/jbe@1309 | 155 end | 
| bsw/jbe@1309 | 156 | 
| bsw/jbe@1309 | 157 if system_application then | 
| bsw/jbe@1309 | 158   if system_application.permitted_scope then | 
| bsw/jbe@1309 | 159     local permitted_scopes = {} | 
| bsw/jbe@1309 | 160     for scope in string.gmatch(system_application.permitted_scope, "[^ ]+") do | 
| bsw/jbe@1309 | 161       permitted_scopes[scope] = true | 
| bsw/jbe@1309 | 162     end | 
| bsw/jbe@1309 | 163     for scope in pairs(requested_scopes) do | 
| bsw/jbe@1309 | 164       if not permitted_scopes[scope] then | 
| bsw/jbe@1309 | 165         return error_redirect("invalid_scope", "Scope not permitted") | 
| bsw/jbe@1309 | 166       end | 
| bsw/jbe@1309 | 167     end | 
| bsw/jbe@1309 | 168   end | 
| bsw/jbe@1309 | 169   if system_application.forbidden_scope then | 
| bsw/jbe@1309 | 170     for scope in string.gmatch(system_application.forbidden_scope, "[^ ]+") do | 
| bsw/jbe@1309 | 171       if requested_scopes[scope] then | 
| bsw/jbe@1309 | 172         return error_redirect("invalid_scope", "Scope forbidden") | 
| bsw/jbe@1309 | 173       end | 
| bsw/jbe@1309 | 174     end | 
| bsw/jbe@1309 | 175   end | 
| bsw/jbe@1309 | 176   if system_application.automatic_scope then | 
| bsw/jbe@1309 | 177     for scope in string.gmatch(system_application.automatic_scope, "[^ ]+") do | 
| bsw/jbe@1309 | 178       scopes_to_accept[scope] = nil | 
| bsw/jbe@1309 | 179       accepted_scopes[scope] = true | 
| bsw/jbe@1309 | 180     end | 
| bsw/jbe@1309 | 181   end | 
| bsw/jbe@1309 | 182   if member_application then | 
| bsw/jbe@1309 | 183     for scope in string.gmatch(member_application.scope, "[^ ]+") do | 
| bsw/jbe@1309 | 184       scopes_to_accept[scope] = nil | 
| bsw/jbe@1309 | 185       accepted_scopes[scope] = true | 
| bsw/jbe@1309 | 186     end | 
| bsw/jbe@1309 | 187   end | 
| bsw/jbe@1309 | 188 else | 
| bsw/jbe@1309 | 189   if dynamic_application_check == "missing_scope" then | 
| bsw/jbe@1309 | 190     return error_redirect("invalid_scope", "Scope not permitted") | 
| bsw/jbe@1309 | 191   end | 
| bsw/jbe@1309 | 192 end | 
| bsw/jbe@1309 | 193 | 
| bsw/jbe@1309 | 194 if next(scopes_to_accept) then | 
| bsw/jbe@1309 | 195   ui.title("Application authorization") | 
| bsw/jbe@1309 | 196   ui.section(function() | 
| bsw/jbe@1309 | 197     ui.sectionHead(function() | 
| bsw/jbe@1309 | 198       ui.heading{ content = client_name } | 
| bsw/jbe@1309 | 199       ui.heading{ content = "wants to access your account" } | 
| bsw/jbe@1309 | 200     end) | 
| bsw/jbe@1309 | 201     if not system_application and not member_application then | 
| bsw/jbe@1309 | 202       ui.sectionRow(function() | 
| bsw/jbe@1309 | 203         ui.container{ content = _"Warning: Untrusted third party application." } | 
| bsw/jbe@1309 | 204       end) | 
| bsw/jbe@1309 | 205     end | 
| bsw/jbe@1309 | 206     ui.sectionRow(function() | 
| bsw/jbe@1309 | 207       ui.heading{ level = 3, content = _"Requested privileges:" } | 
| bsw/jbe@1309 | 208       ui.tag{ tag = "ul", attr = { class = "ul" }, content = function() | 
| bsw/jbe@1309 | 209         for i, entry in ipairs(config.oauth2.available_scopes) do | 
| bsw/jbe@1309 | 210           local name = entry.name[locale.get("lang")] or entry.scope | 
| bsw/jbe@1309 | 211           if accepted_scopes[entry.scope] or requested_scopes[entry.scope] or accepted_scopes[entry.scope .. "_detached"] or requested_scopes[entry.scope .. "_detached"] then | 
| bsw/jbe@1309 | 212             ui.tag{ tag = "li", content = function() | 
| bsw/jbe@1309 | 213               ui.tag{ content = name } | 
| bsw/jbe@1309 | 214               if accepted_scopes[entry.scope .. "_detached"] or requested_scopes[entry.scope .. "_detached"] then | 
| bsw/jbe@1309 | 215                 slot.put(" ") | 
| bsw/jbe@1309 | 216                 ui.tag{ content = _"(detached)" } | 
| bsw/jbe@1309 | 217               end | 
| bsw/jbe@1309 | 218               if scopes_to_accept[entry.scope] or scopes_to_accept[entry.scope .. "_detached"] then | 
| bsw/jbe@1309 | 219                 slot.put(" ") | 
| bsw/jbe@1309 | 220                 ui.tag{ content = _"(new)" } | 
| bsw/jbe@1309 | 221               end | 
| bsw/jbe@1309 | 222               -- TODO display changes | 
| bsw/jbe@1309 | 223             end } | 
| bsw/jbe@1309 | 224           end | 
| bsw/jbe@1309 | 225         end | 
| bsw/jbe@1309 | 226       end } | 
| bsw/jbe@1309 | 227     end ) | 
| bsw/jbe@1309 | 228     local params = { | 
| bsw/jbe@1309 | 229       system_application_id = system_application and system_application.id or nil, | 
| bsw/jbe@1309 | 230       domain = domain, | 
| bsw/jbe@1309 | 231       redirect_uri = redirect_uri, | 
| bsw/jbe@1309 | 232       redirect_uri_explicit = redirect_uri_explicit, | 
| bsw/jbe@1309 | 233       state = state, | 
| bsw/jbe@1309 | 234       response_type = response_type | 
| bsw/jbe@1309 | 235     } | 
| bsw/jbe@1309 | 236     for i = 0, #scopes do | 
| bsw/jbe@1309 | 237       params["scope" .. i] = scopes[i] | 
| bsw/jbe@1309 | 238     end | 
| bsw/jbe@1309 | 239     ui.form{ | 
| bsw/jbe@1309 | 240       module = "oauth2", action = "accept_scope", params = params, | 
| bsw/jbe@1309 | 241       routing = { default = { mode = "redirect", module = "oauth2", view = "authorization", params = request.get_param_strings() } }, | 
| bsw/jbe@1309 | 242       content = function() | 
| bsw/jbe@1309 | 243         ui.sectionRow(function() | 
| bsw/jbe@1309 | 244           ui.submit{ text = _"Grant authorization", attr = { class = "mdl-button mdl-js-button mdl-button--raised mdl-js-ripple-effect mdl-button--colored " } } | 
| bsw/jbe@1309 | 245           slot.put("   ") | 
| bsw/jbe@1309 | 246           ui.link{ content = _"Decline authorization", attr = { class = "mdl-button mdl-js-button" }, external = redirect_uri, params = { error = "access_denied", error_description = "User declined to authorize client" } } | 
| bsw/jbe@1309 | 247         end ) | 
| bsw/jbe@1309 | 248       end | 
| bsw/jbe@1309 | 249     } | 
| bsw/jbe@1309 | 250   end ) | 
| bsw/jbe@1309 | 251 else | 
| bsw/jbe@1309 | 252 | 
| bsw/jbe@1309 | 253   execute.chunk{ module = "oauth2", chunk = "_authorization", params = { | 
| bsw/jbe@1309 | 254     member_id = app.session.member_id, | 
| bsw/jbe@1309 | 255     system_application_id = system_application and system_application.id or nil, | 
| bsw/jbe@1309 | 256     domain = domain, | 
| bsw/jbe@1309 | 257     session_id = app.session.id, | 
| bsw/jbe@1309 | 258     redirect_uri = redirect_uri, | 
| bsw/jbe@1309 | 259     redirect_uri_explicit = redirect_uri_explicit, | 
| bsw/jbe@1309 | 260     scopes = scopes, | 
| bsw/jbe@1309 | 261     state = state, | 
| bsw/jbe@1309 | 262     response_type = response_type | 
| bsw/jbe@1309 | 263   } } | 
| bsw/jbe@1309 | 264 | 
| bsw/jbe@1309 | 265 | 
| bsw/jbe@1309 | 266 end | 
| bsw/jbe@1309 | 267 |