liquid_feedback_frontend

diff app/main/oauth2/authorization.lua @ 1309:32cc544d5a5b

Cumulative patch for upcoming frontend version 4
author bsw/jbe
date Sun Jul 15 14:07:29 2018 +0200 (2018-07-15)
parents
children 2dc0ea4cdb4a
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/app/main/oauth2/authorization.lua	Sun Jul 15 14:07:29 2018 +0200
     1.3 @@ -0,0 +1,267 @@
     1.4 +local function show_error(text)
     1.5 +  ui.title("Authorization")
     1.6 +  ui.section(function()
     1.7 +    ui.sectionHead(function()
     1.8 +      ui.heading{ content = _"Error during authorization" }
     1.9 +    end)
    1.10 +    ui.sectionRow(function()
    1.11 +      ui.container{ content = text }
    1.12 +    end )
    1.13 +  end )
    1.14 +end
    1.15 +
    1.16 +local client_id = param.get("client_id")
    1.17 +local redirect_uri = param.get("redirect_uri")
    1.18 +local redirect_uri_explicit = redirect_uri and true or false
    1.19 +local response_type = param.get("response_type")
    1.20 +local state = param.get("state")
    1.21 +
    1.22 +local no_scope_requested = true
    1.23 +
    1.24 +local scopes = {
    1.25 +  [0] = param.get("scope")
    1.26 +}
    1.27 +
    1.28 +for i = 1, math.huge do
    1.29 +  scopes[i] = param.get("scope" .. i)
    1.30 +  if not scopes[i] then
    1.31 +    break
    1.32 +  end
    1.33 +end
    1.34 +
    1.35 +if #scopes == 0 and not scopes[0] then
    1.36 +  scopes[0] = "identification"
    1.37 +end
    1.38 +
    1.39 +local requested_scopes = {}
    1.40 +
    1.41 +for i = 0, #scopes do
    1.42 +  if scopes[i] then
    1.43 +    for scope in string.gmatch(scopes[i], "[^ ]+") do
    1.44 +      requested_scopes[scope] = true
    1.45 +    end
    1.46 +  end
    1.47 +end
    1.48 +
    1.49 +local system_application
    1.50 +local member_application
    1.51 +local client_name
    1.52 +local scopes_to_accept = table.new(requested_scopes)
    1.53 +local accepted_scopes = {}
    1.54 +
    1.55 +local domain
    1.56 +
    1.57 +if client_id then
    1.58 +  domain = string.match(client_id, "^dynamic:([a-z0-9.-]+)$")
    1.59 +end
    1.60 +
    1.61 +local dynamic_application_check
    1.62 +if domain then
    1.63 +  if #domain > 255 then
    1.64 +    return show_error(_"Domain too long")
    1.65 +  end
    1.66 +  if string.find(domain, "^%.") or string.find(domain, "%.$") or string.find(domain, "%.%.") then
    1.67 +    return show_error(_"Invalid domain format")
    1.68 +  end
    1.69 +  if redirect_uri then
    1.70 +    local redirect_uri_domain, magic = string.match(redirect_uri, "^[Hh][Tt][Tt][Pp][Ss]://([A-Za-z0-9_.-]+)/(.*)$")
    1.71 +    if not redirect_uri_domain or string.lower(redirect_uri_domain) ~= domain or magic ~= config.oauth2.endpoint_magic then
    1.72 +      return show_error(_"Redirect URI forbidden")
    1.73 +    end
    1.74 +  else
    1.75 +    redirect_uri = "https://" .. domain .. "/" .. config.oauth2.endpoint_magic
    1.76 +  end
    1.77 +  dynamic_application_check = DynamicApplicationScope:check_scopes(domain, redirect_uri, response_type, requested_scopes)
    1.78 +  if dynamic_application_check == "not_registered" then
    1.79 +    return show_error(_"Redirect URI or response type not registered")
    1.80 +  end
    1.81 +  client_name = domain
    1.82 +  member_application = MemberApplication:by_member_id_and_domain(app.session.member_id, domain)
    1.83 +  if member_application then
    1.84 +    for scope in string.gmatch(member_application.scope, "[^ ]+") do
    1.85 +      accepted_scopes[scope] = true
    1.86 +      scopes_to_accept[scope] = nil
    1.87 +    end
    1.88 +  end
    1.89 +else
    1.90 +  system_application = SystemApplication:by_client_id(client_id)
    1.91 +  if system_application then
    1.92 +    if redirect_uri_explicit then
    1.93 +      if 
    1.94 +        redirect_uri ~= system_application.default_redirect_uri 
    1.95 +        and not SystemApplicationRedirectUri:by_pk(system_application.id, redirect_uri) 
    1.96 +      then
    1.97 +        return show_error(_"Redirect URI invalid")
    1.98 +      end
    1.99 +    else
   1.100 +      redirect_uri = system_application.default_redirect_uri
   1.101 +    end
   1.102 +    if system_application.flow ~= response_type then
   1.103 +      return show_error(_"Response type not allowed for given client")
   1.104 +    end
   1.105 +    client_name = system_application.name
   1.106 +    member_application = MemberApplication:by_member_id_and_system_application_id(app.session.member_id, system_application.id)
   1.107 +  end
   1.108 +end
   1.109 +
   1.110 +if not client_name then
   1.111 +  return show_error(_"Client ID invalid")
   1.112 +end
   1.113 +
   1.114 +local function error_redirect(error_code, description)
   1.115 +  local params = {
   1.116 +    state = state,
   1.117 +    error = error_code,
   1.118 +    error_description = description
   1.119 +  }
   1.120 +  if response_type == "token" then
   1.121 +    local anchor_params_list = {}
   1.122 +    for k, v in pairs(params) do
   1.123 +      anchor_params_list[#anchor_params_list+1] = k .. "=" .. encode.url_part(v)
   1.124 +    end
   1.125 +    local anchor = table.concat(anchor_params_list, "&")
   1.126 +    request.redirect{
   1.127 +      external = redirect_uri .. "#" .. anchor
   1.128 +    }
   1.129 +  else
   1.130 +    request.redirect{ 
   1.131 +      external = redirect_uri,
   1.132 +      params = params
   1.133 +    }
   1.134 +  end
   1.135 +end
   1.136 +
   1.137 +if response_type ~= "code" and response_type ~= "token" then
   1.138 +  return error_redirect("unsupported_response_type", "Invalid response type")
   1.139 +end
   1.140 +
   1.141 +for i = 0, #scopes do
   1.142 +  if scopes[i] == "" then
   1.143 +    return error_redirect("invalid_scope", "Empty scope requested")
   1.144 +  end
   1.145 +end
   1.146 +
   1.147 +for scope in pairs(requested_scopes) do
   1.148 +  local scope_valid = false
   1.149 +  for i, entry in ipairs(config.oauth2.available_scopes) do
   1.150 +    if scope == entry.scope or scope == entry.scope .. "_detached" then
   1.151 +      scope_valid = true
   1.152 +      break
   1.153 +    end
   1.154 +  end
   1.155 +  if not scope_valid then
   1.156 +    return error_redirect("invalid_scope", "Requested scope not available")
   1.157 +  end
   1.158 +end
   1.159 +
   1.160 +if system_application then
   1.161 +  if system_application.permitted_scope then
   1.162 +    local permitted_scopes = {}
   1.163 +    for scope in string.gmatch(system_application.permitted_scope, "[^ ]+") do
   1.164 +      permitted_scopes[scope] = true
   1.165 +    end
   1.166 +    for scope in pairs(requested_scopes) do
   1.167 +      if not permitted_scopes[scope] then
   1.168 +        return error_redirect("invalid_scope", "Scope not permitted")
   1.169 +      end
   1.170 +    end
   1.171 +  end
   1.172 +  if system_application.forbidden_scope then
   1.173 +    for scope in string.gmatch(system_application.forbidden_scope, "[^ ]+") do
   1.174 +      if requested_scopes[scope] then
   1.175 +        return error_redirect("invalid_scope", "Scope forbidden")
   1.176 +      end
   1.177 +    end
   1.178 +  end
   1.179 +  if system_application.automatic_scope then
   1.180 +    for scope in string.gmatch(system_application.automatic_scope, "[^ ]+") do
   1.181 +      scopes_to_accept[scope] = nil
   1.182 +      accepted_scopes[scope] = true
   1.183 +    end
   1.184 +  end
   1.185 +  if member_application then
   1.186 +    for scope in string.gmatch(member_application.scope, "[^ ]+") do
   1.187 +      scopes_to_accept[scope] = nil
   1.188 +      accepted_scopes[scope] = true
   1.189 +    end
   1.190 +  end
   1.191 +else
   1.192 +  if dynamic_application_check == "missing_scope" then
   1.193 +    return error_redirect("invalid_scope", "Scope not permitted")
   1.194 +  end
   1.195 +end
   1.196 +
   1.197 +if next(scopes_to_accept) then
   1.198 +  ui.title("Application authorization")
   1.199 +  ui.section(function()
   1.200 +    ui.sectionHead(function()
   1.201 +      ui.heading{ content = client_name }
   1.202 +      ui.heading{ content = "wants to access your account" }
   1.203 +    end)
   1.204 +    if not system_application and not member_application then
   1.205 +      ui.sectionRow(function()
   1.206 +        ui.container{ content = _"Warning: Untrusted third party application." }
   1.207 +      end)
   1.208 +    end
   1.209 +    ui.sectionRow(function()
   1.210 +      ui.heading{ level = 3, content = _"Requested privileges:" }
   1.211 +      ui.tag{ tag = "ul", attr = { class = "ul" }, content = function()
   1.212 +        for i, entry in ipairs(config.oauth2.available_scopes) do
   1.213 +          local name = entry.name[locale.get("lang")] or entry.scope
   1.214 +          if accepted_scopes[entry.scope] or requested_scopes[entry.scope] or accepted_scopes[entry.scope .. "_detached"] or requested_scopes[entry.scope .. "_detached"] then
   1.215 +            ui.tag{ tag = "li", content = function()
   1.216 +              ui.tag{ content = name }
   1.217 +              if accepted_scopes[entry.scope .. "_detached"] or requested_scopes[entry.scope .. "_detached"] then
   1.218 +                slot.put(" ")
   1.219 +                ui.tag{ content = _"(detached)" }
   1.220 +              end
   1.221 +              if scopes_to_accept[entry.scope] or scopes_to_accept[entry.scope .. "_detached"] then
   1.222 +                slot.put(" ")
   1.223 +                ui.tag{ content = _"(new)" }
   1.224 +              end
   1.225 +              -- TODO display changes
   1.226 +            end }
   1.227 +          end
   1.228 +        end
   1.229 +      end }
   1.230 +    end )
   1.231 +    local params = {
   1.232 +      system_application_id = system_application and system_application.id or nil,
   1.233 +      domain = domain,
   1.234 +      redirect_uri = redirect_uri,
   1.235 +      redirect_uri_explicit = redirect_uri_explicit,
   1.236 +      state = state,
   1.237 +      response_type = response_type
   1.238 +    }
   1.239 +    for i = 0, #scopes do
   1.240 +      params["scope" .. i] = scopes[i]
   1.241 +    end
   1.242 +    ui.form{
   1.243 +      module = "oauth2", action = "accept_scope", params = params,
   1.244 +      routing = { default = { mode = "redirect", module = "oauth2", view = "authorization", params = request.get_param_strings() } },
   1.245 +      content = function()
   1.246 +        ui.sectionRow(function()
   1.247 +          ui.submit{ text = _"Grant authorization", attr = { class = "mdl-button mdl-js-button mdl-button--raised mdl-js-ripple-effect mdl-button--colored " } }
   1.248 +          slot.put("   ")
   1.249 +          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" } }
   1.250 +        end )
   1.251 +      end
   1.252 +    }
   1.253 +  end )
   1.254 +else
   1.255 +  
   1.256 +  execute.chunk{ module = "oauth2", chunk = "_authorization", params = {
   1.257 +    member_id = app.session.member_id, 
   1.258 +    system_application_id = system_application and system_application.id or nil, 
   1.259 +    domain = domain, 
   1.260 +    session_id = app.session.id, 
   1.261 +    redirect_uri = redirect_uri, 
   1.262 +    redirect_uri_explicit = redirect_uri_explicit, 
   1.263 +    scopes = scopes, 
   1.264 +    state = state,
   1.265 +    response_type = response_type
   1.266 +  } }
   1.267 +
   1.268 +
   1.269 +end
   1.270 +

Impressum / About Us