unifiHack: init
This package takes the upstream Unifi package, and then applies a AspectJ aspect which replaces the auth logic with stuff which checks whether there's a Pomerium header.
This commit is contained in:
parent
2d52be000f
commit
85c2c4d507
4 changed files with 260 additions and 0 deletions
|
@ -18,6 +18,7 @@
|
|||
envoy = import ./envoy args;
|
||||
deluge = import ./deluge args;
|
||||
grafana-plugins = import ./grafana-plugins args;
|
||||
unifiHacked = import ./unifi-hack args;
|
||||
tiny-remapper = import ./tiny-remapper.nix args;
|
||||
} // (import ./heptapod-runner.nix args)
|
||||
// (import ./lightspeed args)
|
||||
|
|
89
nix/pkgs/unifi-hack/aspect/MyLogin.aj
Normal file
89
nix/pkgs/unifi-hack/aspect/MyLogin.aj
Normal file
|
@ -0,0 +1,89 @@
|
|||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import org.slf4j.Logger;
|
||||
import com.ubnt.data.Admin;
|
||||
import com.ubnt.data.X;
|
||||
import com.ubnt.ace.view.AuthFilter;
|
||||
import com.ubnt.service.DatabaseService;
|
||||
import com.ubnt.data.oOOO.C;
|
||||
import com.ubnt.data.oOOO.Filter;
|
||||
import com.ubnt.service.system.SystemInitializingBean;
|
||||
|
||||
aspect MyLogin {
|
||||
private static final boolean LOG_STUFF = false;
|
||||
|
||||
private static final void log(String stuff) {
|
||||
if (!LOG_STUFF) return;
|
||||
SystemInitializingBean.SYSTEM_LOGGER.error(stuff);
|
||||
}
|
||||
|
||||
static final X doLoginByHeader(HttpServletRequest request, HttpServletResponse response, X context) {
|
||||
log("in doLoginByHeader");
|
||||
String currentEmail = "";
|
||||
if (context != null) {
|
||||
Admin adminData = (Admin)context.getX("admin", X.EMPTY_INSTANCE);
|
||||
currentEmail = adminData.getString("email");
|
||||
}
|
||||
log("currentEmail: " + currentEmail);
|
||||
String proxyEmail = request.getHeader("x-pomerium-claim-email");
|
||||
if (proxyEmail == null) {
|
||||
proxyEmail = "";
|
||||
}
|
||||
log("proxyEmail: " + proxyEmail);
|
||||
if (!currentEmail.equals(proxyEmail)) {
|
||||
// We're not logged in as who we should be.
|
||||
if (proxyEmail.equals("")) {
|
||||
// We should be logged out.
|
||||
log("logging out");
|
||||
AuthFilter.getInner().logout(request, response);
|
||||
return null;
|
||||
} else {
|
||||
// We should be logged in as somebody.
|
||||
if (!currentEmail.equals("")) {
|
||||
log("logging out first");
|
||||
// We have an existing session; log out first.
|
||||
AuthFilter.getInner().logout(request, response);
|
||||
}
|
||||
|
||||
// Now log in as proxyEmail.
|
||||
Admin newAdmin = null;
|
||||
for (final Admin admin : DatabaseService.database().<Admin>queryList(Admin.class, new C.Factory().or(Filter.equals("name", proxyEmail), Filter.equals("email", proxyEmail)).create())) {
|
||||
newAdmin = admin; break;
|
||||
}
|
||||
if (newAdmin == null) {
|
||||
// TODO: make this create a new user account?
|
||||
log("couldn't find admin user email " + proxyEmail);
|
||||
throw new NoMatchingUser();
|
||||
}
|
||||
log("got admin with email " + proxyEmail);
|
||||
return AuthFilter.getInner().createSession(newAdmin, false, false, request, response);
|
||||
}
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
pointcut authFilter(ServletRequest request, ServletResponse response, FilterChain filterChain): within(com.ubnt.ace.view.AuthFilter) && execution(void com.ubnt.ace.view.AuthFilter.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)) && args(request, response, filterChain);
|
||||
pointcut thePart(HttpServletRequest httpRequest, ServletRequest request, ServletResponse response, FilterChain filterChain): within(com.ubnt.ace.view.AuthFilter) && call(X com.ubnt.ace.view.AuthFilter.inner.getAnXGivenARequestSomehow(HttpServletRequest)) && args(httpRequest) && cflow(authFilter(request, response, filterChain));
|
||||
|
||||
// Wrap the part where we fetch the current user.
|
||||
X around(HttpServletRequest httpRequest, ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain): thePart(httpRequest, servletRequest, servletResponse, filterChain) {
|
||||
X originalContext = proceed(httpRequest, servletRequest, servletResponse, filterChain);
|
||||
return doLoginByHeader((HttpServletRequest)servletRequest, (HttpServletResponse)servletResponse, originalContext);
|
||||
}
|
||||
|
||||
// Add some extra exception handling.
|
||||
void around(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws java.io.IOException: authFilter(servletRequest, servletResponse, filterChain) {
|
||||
try {
|
||||
proceed(servletRequest, servletResponse, filterChain);
|
||||
} catch (NoMatchingUser e) {
|
||||
HttpServletResponse response = (HttpServletResponse)servletResponse;
|
||||
response.sendError(HttpServletResponse.SC_FORBIDDEN, "You probably don't have a user account with a matching email.");
|
||||
}
|
||||
}
|
||||
|
||||
public static class NoMatchingUser extends java.lang.RuntimeException {}
|
||||
|
||||
}
|
122
nix/pkgs/unifi-hack/aspect/ace.jar.tiny2map
Normal file
122
nix/pkgs/unifi-hack/aspect/ace.jar.tiny2map
Normal file
|
@ -0,0 +1,122 @@
|
|||
tiny 2 0 intermediary named
|
||||
c com/ubnt/ace/api/ApiUtils com/ubnt/ace/api/ApiUtils
|
||||
c com/ubnt/data/X com/ubnt/data/X
|
||||
f Ljava/lang/String; Ô00000 ATTR_PREFIX_X
|
||||
f Lorg/slf4j/Logger; õ00000 LOGGER
|
||||
f Ljava/lang/String; Õo0000 MONGO_GTE
|
||||
f Ljava/lang/String; ÕÒ0000 MONGO_ALL
|
||||
f Ljava/lang/String; o00000 ATTR_NO_DELETE
|
||||
f Ljava/lang/String; õO0000 ATTR_HIDDEN_ID
|
||||
f Ljava/lang/String; ö00000 ATTR_HIDDEN
|
||||
f Ljava/util/List; ÕO0000 EMPTY_LIST
|
||||
f Ljava/lang/String; ôO0000 MONGO_INC
|
||||
f Ljava/lang/String; ÔÒ0000 MONGO_EXISTS
|
||||
f Ljava/lang/String; ôo0000 MONGO_IN
|
||||
f Ljava/util/List; oÒ0000 EMPTY_INTEGER_LIST
|
||||
f Ljava/lang/String; public ATTR_NO_EDIT
|
||||
f Lcom/ubnt/data/X; ÖÒ0000 EMPTY_INSTANCE
|
||||
f Ljava/lang/String; voidnew MONGO_TYPE
|
||||
f Ljava/lang/String; öÒ0000 MONGO_PULL
|
||||
f Ljava/lang/String; interfacesuper MONGO_REGEX
|
||||
f Ljava/lang/String; õÒ0000 MONGO_SET
|
||||
f Ljava/lang/String; ÒO0000 MONGO_RENAME
|
||||
f Ljava/lang/String; Object MONGO_NE
|
||||
f Lcom/fasterxml/jackson/databind/ObjectMapper; Õ00000 JACKSON_OBJECTMAPPER
|
||||
f Ljava/lang/String; õo0000 MONGO_OR
|
||||
f Ljava/util/List; ÖO0000 EMPTY_CLASS_LIST
|
||||
f Ljava/lang/String; intsuper ID_ATTR_NAME
|
||||
f Ljava/lang/String; oO0000 MONGO_NIN
|
||||
f Ljava/lang/String; dosuper MONGO_LT
|
||||
f Ljava/lang/String; ô00000 MONGO_UNSET
|
||||
f Ljava/lang/String; float MONGO_GT
|
||||
f Ljava/util/List; supersuper EMPTY_STRING_LIST
|
||||
c com/ubnt/data/oOOO/o0OO com/ubnt/data/oOOO/Filter
|
||||
m (Ljava/lang/String;Ljava/lang/Object;)Lcom/ubnt/data/oOOO/o0OO; Õ00000 equals
|
||||
c com/ubnt/ace/void com/ubnt/ace/void
|
||||
m (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; õ00000 hashPassword
|
||||
m (Ljavax/servlet/http/HttpServletResponse;Ljava/lang/String;Ljava/lang/String;)V o00000 setCookie
|
||||
m (Ljavax/servlet/http/HttpServletResponse;Ljava/lang/String;Ljava/lang/String;Z)V o00000 setCookie
|
||||
c com/ubnt/ace/api/ApiServlet com/ubnt/ace/api/ApiServlet
|
||||
m (Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)Lcom/ubnt/ace/api/B; o00000 serviceAuthless
|
||||
m (Ljava/lang/String;)Z o00000 skipAuthForPath
|
||||
c com/ubnt/service/system/OOOoOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO com/ubnt/service/system/OOOoOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
|
||||
m (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lcom/ubnt/data/X;)V o00000 addToCache
|
||||
c com/ubnt/data/oOOO/C com/ubnt/data/oOOO/C
|
||||
c com/ubnt/data/oOOO/C$_o com/ubnt/data/oOOO/C$Factory
|
||||
m (Ljava/lang/String;Ljava/lang/Object;)Lcom/ubnt/data/oOOO/C$_o; Ô00000 equals
|
||||
m ()Lcom/ubnt/data/oOOO/C; super create
|
||||
m ([Lcom/ubnt/data/oOOO/o0OO;)Lcom/ubnt/data/oOOO/C$_o; Ò00000 or
|
||||
c com/ubnt/service/system/O00o com/ubnt/service/system/DatabaseBean
|
||||
m (Ljava/lang/Class;Lcom/ubnt/data/oOOO/C;)V return deleteFromDB
|
||||
m (Ljava/lang/Class;Lcom/ubnt/data/oOOO/C;)Ljava/util/List; new queryList
|
||||
c com/ubnt/service/OoOO com/ubnt/service/DatabaseService
|
||||
m ()Lcom/ubnt/service/system/O00o; õ00000 database
|
||||
c com/ubnt/ace/view/AuthFilter com/ubnt/ace/view/AuthFilter
|
||||
m (Ljavax/servlet/http/HttpServletRequest;)V Ô00000 checkDemoRequest
|
||||
f [Ljava/lang/String; Ò00000 skipAuthUcorePaths
|
||||
m (Ljavax/servlet/http/HttpServletRequest;Lcom/ubnt/data/X;)Lcom/ubnt/data/X; o00000 makeContext
|
||||
f Lcom/ubnt/ace/view/AuthFilter$_Oo; Ø00000 innerInstance
|
||||
f Lorg/slf4j/Logger; õ00000 logger
|
||||
m ()Lcom/ubnt/ace/view/AuthFilter$_Oo; new getInner
|
||||
f Ljava/lang/String; ø00000 DEMO
|
||||
f Ljava/lang/String; null CONTEXT
|
||||
f [Ljava/lang/String; Ö00000 skipAuthPaths
|
||||
f J ö00000 thingyVersion
|
||||
m (Ljavax/servlet/http/HttpServletRequest;)Z new isDemoRequest
|
||||
f [Ljava/lang/String; Õ00000 requireAuthPaths
|
||||
m (Ljava/lang/String;)Z o00000 authRequired
|
||||
c com/ubnt/ace/view/AuthFilter$_Oo com/ubnt/ace/view/AuthFilter$inner
|
||||
m (Ljavax/servlet/http/HttpServletRequest;)Lcom/ubnt/data/X; o00000 getAnXGivenARequestSomehow
|
||||
m (Lcom/ubnt/data/Admin;ZZLjava/lang/String;Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)Lcom/ubnt/data/X; Ò00000 createSSession
|
||||
p 3 bl2 strict
|
||||
p 2 bl remember
|
||||
p 4 string accessToken
|
||||
m (Ljava/lang/String;Lcom/ubnt/data/X;)V Õ00000 saveAccessTokenToCache
|
||||
p 1 string accessToken
|
||||
f Ljava/lang/String; Ô00000 SESSION_COOKIE_NAME
|
||||
m (Ljava/lang/String;)Lcom/ubnt/data/X; float checkPrivilege
|
||||
p 1 string adminId
|
||||
f Ljava/lang/String; Ò00000 CSRF_TOKEN_NAME
|
||||
m (Ljava/lang/String;Ljava/lang/String;Ljava/util/Optional;)Lcom/ubnt/data/Admin; o00000 login
|
||||
p 2 string2 password
|
||||
p 3 optional twoFAToken
|
||||
p 1 string1 username
|
||||
m (Lcom/ubnt/data/Admin;Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)Lcom/ubnt/data/X; o00000 createSessionMaybe
|
||||
m (Ljava/lang/String;)Lcom/ubnt/data/X; o00000 loadSessionById
|
||||
m (Lcom/ubnt/data/Admin;ZZLjava/lang/String;Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)Lcom/ubnt/data/X; o00000 createSession
|
||||
p 2 bl remember
|
||||
p 3 bl2 strict
|
||||
p 4 string accessToken
|
||||
m (Lcom/ubnt/data/Admin;ZZLjavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)Lcom/ubnt/data/X; o00000 createSession
|
||||
p 2 boolean2 remember
|
||||
p 3 boolean3 strict
|
||||
m (Ljava/lang/String;)Lcom/ubnt/data/X; ö00000 invalidateAccessToken
|
||||
m (Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)V o00000 logout
|
||||
c com/ubnt/ace/api/for com/ubnt/ace/api/ApiException
|
||||
f Lcom/ubnt/ace/api/for; Ô00000 NotFound
|
||||
f Lcom/ubnt/data/X; ÕO0000 errorData
|
||||
f Lcom/ubnt/ace/api/for; Ö00000 NotImplemented
|
||||
f Lcom/ubnt/ace/api/for; oO0000 DeviceBusy
|
||||
f Lcom/ubnt/ace/api/for; supersuper NoApplicableDevices
|
||||
f Lcom/ubnt/ace/api/for; o00000 NoDelete
|
||||
f Lcom/ubnt/ace/api/for; ÖO0000 LoginRequired
|
||||
f Lcom/ubnt/ace/api/for; OO0000 NotSupported
|
||||
f Lcom/ubnt/ace/api/for; ö00000 NotCompatible
|
||||
f Lcom/ubnt/ace/api/for; õO0000 NoEdit
|
||||
m ()Lcom/ubnt/data/X; o00000 getErrorData
|
||||
f Lcom/ubnt/ace/api/for; Ò00000 UpgradeInProgress
|
||||
f Lcom/ubnt/ace/api/for; Oo0000 InvalidArgs
|
||||
f Lcom/ubnt/ace/api/for; public EmptyGoogleApiKey
|
||||
f Lcom/ubnt/ace/api/for; classsuper IdInvalid
|
||||
f Lcom/ubnt/ace/api/for; void Invalid
|
||||
f Lcom/ubnt/ace/api/for; ô00000 InvalidLogin
|
||||
f Lcom/ubnt/ace/api/for; float NoPermission
|
||||
f Lcom/ubnt/ace/api/for; ÔO0000 MissingUbbDevice
|
||||
f Lcom/ubnt/ace/api/for; Õ00000 IdRequired
|
||||
f Lcom/ubnt/ace/api/for; ôO0000 NotUDM
|
||||
f Lcom/ubnt/ace/api/for; õ00000 NotCloudKey
|
||||
f Lcom/ubnt/ace/api/for; öO0000 InvalidPayload
|
||||
f Lcom/ubnt/ace/api/for; ÒO0000 LocateSiteFailed
|
||||
f Lcom/ubnt/ace/api/for; Object NoSuchCommand
|
||||
c com/ubnt/service/system/O0oO com/ubnt/service/system/SystemInitializingBean
|
||||
f Lorg/slf4j/Logger; õo0000 SYSTEM_LOGGER
|
48
nix/pkgs/unifi-hack/default.nix
Normal file
48
nix/pkgs/unifi-hack/default.nix
Normal file
|
@ -0,0 +1,48 @@
|
|||
{ depot, pkgs, ... }:
|
||||
let
|
||||
inherit (pkgs) stdenvNoCC jdk aspectj unifi;
|
||||
in
|
||||
stdenvNoCC.mkDerivation {
|
||||
pname = "unifi-hack";
|
||||
version = "depot";
|
||||
|
||||
src = ./aspect;
|
||||
|
||||
nativeBuildInputs = [ jdk aspectj depot.pkgs.tiny-remapper ];
|
||||
|
||||
buildPhase = ''
|
||||
mkdir $NIX_BUILD_TOP/tmp
|
||||
|
||||
cp $(type -p ajc) $NIX_BUILD_TOP/tmp/ajc
|
||||
substituteInPlace $NIX_BUILD_TOP/tmp/ajc \
|
||||
--replace '-Xmx64M' ""
|
||||
|
||||
ajc_classpath="$(ls ${unifi}/lib/*.jar | tr '\n' ':' | sed 's/:$//')"
|
||||
tiny-remapper ${unifi}/lib/ace.jar $NIX_BUILD_TOP/tmp/acedeobf.jar ./ace.jar.tiny2map intermediary named
|
||||
$NIX_BUILD_TOP/tmp/ajc -8 \
|
||||
-classpath "${aspectj}/lib/aspectjrt.jar:$ajc_classpath" \
|
||||
-inpath $NIX_BUILD_TOP/tmp/acedeobf.jar \
|
||||
-outjar $NIX_BUILD_TOP/tmp/aceaspected.jar \
|
||||
./MyLogin.aj
|
||||
tiny-remapper $NIX_BUILD_TOP/tmp/aceaspected.jar $NIX_BUILD_TOP/tmp/acereobf.jar ./ace.jar.tiny2map named intermediary
|
||||
|
||||
# Repack the manifest
|
||||
pushd $NIX_BUILD_TOP/tmp
|
||||
jar xf acereobf.jar META-INF/MANIFEST.MF
|
||||
awk \
|
||||
'/^[^ ]/ { inclasspath=0 } /Class-Path:/ { inclasspath=1 } { if (inclasspath) print; }' \
|
||||
META-INF/MANIFEST.MF > classpath.MF
|
||||
substituteInPlace classpath.MF \
|
||||
--replace 'Class-Path: ' 'Class-Path: aspectjrt.jar '
|
||||
jar -ufm acereobf.jar classpath.MF
|
||||
popd
|
||||
'';
|
||||
|
||||
installPhase = ''
|
||||
cp -R ${unifi} $out
|
||||
chmod -R u+w $out
|
||||
rm $out/lib/ace.jar
|
||||
cp $NIX_BUILD_TOP/tmp/acereobf.jar $out/lib/ace.jar
|
||||
cp ${aspectj}/lib/aspectjrt.jar $out/lib/aspectjrt.jar
|
||||
'';
|
||||
}
|
Loading…
Reference in a new issue