You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Gateway/Proxy/WebFactory.cs

396 lines
16 KiB
C#

using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using UMC.Data;
using UMC.Net;
using UMC.Web;
using UMC.Security;
using UMC.ITME.Entities;
using System.Collections;
// using System.Net;
namespace UMC.ITME
{
[Apiumc(Weight = 0, Desc = "云模块服务组件")]
public class WebFactory : IWebFactory
{
class XHRException : Exception
{
String _t;
public XHRException(String xhr)
{
_t = xhr;
}
public override string ToString()
{
return _t;
}
}
internal static Dictionary<String, Dictionary<String, Tuple<WebAuthType, bool>>> Auths = new Dictionary<string, Dictionary<string, Tuple<WebAuthType, bool>>>();
class XHRActivity : WebActivity
{
public XHRActivity(SiteConfig site)
{
this.site = site;
}
SiteConfig site;
public override void ProcessActivity(WebRequest request, WebResponse response)
{
var user = this.Context.Token.Identity();
var httpProxy = new HttpProxy(site, this.Context.Client.Context, -1, "/", String.Empty);
if (httpProxy.Domain == null)
{
this.Prompt("安全审记", $"此应用临时关闭,请联系应用管理员");
}
switch (site.Site.UserModel)
{
case UserModel.Bridge:
break;
default:
this.Prompt("UMC云模块只支持权限桥接模式");
break;
}
if (Auths.TryGetValue(site.Root, out var _dic) == false)
{
_dic = new Dictionary<string, Tuple<WebAuthType, bool>>();
new Uri(httpProxy.Domain, "/UMC/System/Setup/Mapping").WebRequest().Get(r =>
{
r.ReadAsString(js =>
{
var ds = JSON.Deserialize(js) as Hashtable;
if (ds?.ContainsKey("data") == true)
{
var data = ds["data"] as Array;
foreach (var o in data)
{
var dic = o as Hashtable;
if (dic.ContainsKey("model"))
{
var model = dic["model"] as string;
var auth = dic["auth"] as string;
WebAuthType authType = WebAuthType.All;
switch (auth)
{
case "all":
continue;
case "admin":
authType = WebAuthType.Admin;
break;
case "guest":
authType = WebAuthType.Guest;
break;
case "user":
authType = WebAuthType.User;
break;
case "usercheck":
authType = WebAuthType.UserCheck;
break;
case "check":
authType = WebAuthType.Check;
break;
}
if (dic.ContainsKey("cmd"))
{
_dic[$"{model}.{dic["cmd"]}"] = Tuple.Create(authType, dic.ContainsKey("biometric"));
}
else
{
_dic[model] = Tuple.Create(authType, dic.ContainsKey("biometric"));
}
}
}
Auths[site.Root] = _dic;
}
}, e => { });
});
}
if (this.Context.Items.Contains(typeof(WebFactory)) == false)
{
bool isBiometric;
if (site.Site.IsAuth == true)
{
if (WebClient.Verify(httpProxy.Account, site.Site.SiteKey.Value, request.Model, request.Command, _dic, out isBiometric) == false)
{
if (user.IsAuthenticated)
{
this.Prompt("访问受限,请联系管理员");
}
else
{
this.Context.Send("Login", true);
}
}
}
else
{
if (WebClient.Verify(user, 0, request.Model, request.Command, _dic, out isBiometric) == false)
{
if (user.IsAuthenticated)
{
this.Prompt("访问受限,请联系管理员");
}
else
{
this.Context.Send("Login", true);
}
}
}
if (isBiometric && this.Context.Token.BiometricTime == 0)
{
var send = request.SendValues ?? new WebMeta();
var vs = request.Headers.GetDictionary()[request.Command];
if (vs is string)
{
send.Put("_", vs);
}
else if (vs is string[])
{
var stv = vs as string[];
if (stv.Length > 0)
{
send.Put("__", String.Join('/', stv));
}
}
if (send.Count > 0)
{
response.Redirect("Account", "Biometric", new WebMeta().Put("oauth_callback", $"/UMC/{request.Model}{request.Command}?{UMC.Data.JSON.Serialize(send)}"), true);
}
else
{
response.Redirect("Account", "Biometric", new WebMeta().Put("oauth_callback", $"/UMC/{request.Model}{request.Command}"), true);
}
}
this.Context.Items[typeof(WebFactory)] = true;
}
StringBuilder sb = new StringBuilder();
sb.Append("_model=");
sb.Append(request.Model);
sb.Append("&_cmd=");
sb.Append(request.Command);
var sv = request.SendValues;
if (sv != null)
{
var em = sv.GetDictionary().GetEnumerator();
while (em.MoveNext())
{
sb.Append("&");
sb.Append(Uri.EscapeDataString(em.Key.ToString()));
sb.Append("=");
sb.Append(Uri.EscapeDataString(em.Value.ToString()));
}
}
var values = request.Headers.GetDictionary()[request.Command];
if (values is string)
{
sb.Append("&_=");
sb.Append(Uri.EscapeDataString(values as string));
}
else if (values is string[])
{
var stv = values as string[];
if (stv.Length > 0)
{
sb.Append("&__=");
sb.Append(Uri.EscapeDataString(String.Join('/', stv)));
}
}
var query = request.Url.Query ?? "";
if (query.StartsWith("?_v="))
{
int v = query.IndexOf('&');
if (v > 0)
{
query = query.Substring(0, v);
}
}
var content = this.Context.Client.Context;
var webReq = httpProxy.Reqesut(content.Transfer(httpProxy.Domain));
httpProxy.AuthBridge(webReq.Headers);
// webReq.Headers[]
webReq.Headers["x-forwarded-proto"] = request.Url.Scheme;
webReq.Headers[System.Net.HttpRequestHeader.Host] = request.Url.Authority;
webReq.RawUrl = $"/UMC/{Utility.Guid(this.Context.Token.Device.Value)}/{query}";
webReq.ContentType = "application/x-www-form-urlencoded";
webReq.Post(sb.ToString(), res =>
{
var token = res.Headers.Get("itme-access-token");
if (String.IsNullOrEmpty(token) == false)
{
if (String.Equals(httpProxy.SiteCookie.Cookies, token) == false)
{
httpProxy.SiteCookie.Cookies = token;
httpProxy.IsChangeUser = true;
httpProxy.SaveCookie();
}
}
int StatusCode = (int)res.StatusCode;
HttpProxy.LogWrite(httpProxy.Context, httpProxy.Site, StatusCode, webReq.RawUrl, httpProxy.SiteCookie.Account, null, String.Empty);
if (StatusCode > 300 && StatusCode < 400)
{
var url = res.Headers.Get("Location");
response.Headers.Put("Data", new Uri(content.Url, url));
response.ClientEvent |= (WebEvent)131072;
this.Context.OutputFinish();
}
else if (StatusCode == 200)
{
res.ReadAsString(xhr =>
{
if (xhr.StartsWith("{\"ClientEvent\":"))
{
var xData = JSON.Deserialize(xhr) as Hashtable;
var webEvent = (WebEvent)Utility.Parse(xData["ClientEvent"].ToString(), 0);
response.ClientEvent = webEvent;
if (xData.ContainsKey("Headers"))
{
var header = xData["Headers"] as Hashtable;
var m = header.GetEnumerator();
while (m.MoveNext())
{
response.Headers.Put(m.Key as string, m.Value);
}
}
if (xData.ContainsKey("Redirect"))
{
var redirect = xData["Redirect"] as Hashtable;
var model = redirect["model"] as string;
var cmd = redirect["cmd"] as string;
if (String.IsNullOrEmpty(model) == false && String.IsNullOrEmpty(cmd) == false)
{
this.Context.Items[$"XHR.{model}.{cmd}"] = site.Root;
var send = redirect["send"];
if (send is IDictionary)
{
response.Redirect(model, cmd, new WebMeta(send as IDictionary), false);
}
else if (send is string)
{
response.Redirect(model, cmd, send as string, false);
}
else
{
response.Redirect(model, cmd, false);
}
}
}
}
else
{
response.Headers.Put("Data", Data.JSON.Expression(xhr));
response.ClientEvent |= (WebEvent)131072;
}
this.Context.OutputFinish();
}, error =>
{
if (error is WebAbortException)
{
this.Context.OutputFinish();
}
else
{
this.Context.Client.Context.Error(error);
}
});
}
else
{
// httpProxy.ProcessEnd();
if (res.Error != null)
{
this.Prompt(site.Site.Caption, $"异常:{res.Error.Message}.", false);
this.Context.OutputFinish();
}
else
{
res.Transfer(this.Context.Client.Context);
}
}
});
response.Redirect(Empty);
}
}
class XHRFlow : WebFlow
{
public override WebActivity GetFirstActivity()
{
var m = this.Context.Request.Model;
var cmd = this.Context.Request.Command;
var cgf = Data.Reflection.Configuration("UMC");
var p = cgf[$"{m}.{cmd}"] ?? cgf[$"{m}.*"];
if (p != null)
{
var root = p.Attributes["root"];
var lold = this.Context.Items[$"XHR.{m}.{cmd}"] as string;
if (String.IsNullOrEmpty(root) == false && String.Equals(lold, root) == false)
{
var site = DataFactory.Instance().SiteConfig(p.Attributes["root"]);
if (site != null)
{
if (site.IsClose)
{
if (site.Site.Flag < 0 || site.Domains.Length == 0 || String.IsNullOrEmpty(site.Site.MarketKey))
{
return WebActivity.Empty;
}
else
{
this.Prompt("许可证已过期", $"{site.Site}的许可证已经过期,请联系管理员更新许可证");
}
}
return new XHRActivity(site);
}
}
}
return WebActivity.Empty;
}
}
void IWebFactory.OnInit(WebContext context)
{
}
WebFlow IWebFactory.GetFlow(WebContext context, string mode)
{
return new XHRFlow();
}
}
}