001/* 002 * (C) Copyright 2019 Nuxeo (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * Florent Guillaume 018 */ 019package org.nuxeo.runtime.api.login; 020 021import java.lang.reflect.Constructor; 022import java.security.Principal; 023import java.util.Collections; 024 025import javax.security.auth.Subject; 026import javax.security.auth.login.LoginContext; 027import javax.security.auth.login.LoginException; 028 029import sun.reflect.ReflectionFactory; 030 031/** 032 * An implementation of {@link LoginContext} just holding a principal. 033 * <p> 034 * Construction is done through the static {@link #create} method, which takes a {@link Principal}. The caller must push 035 * the principal on the Nuxeo principal stack using {@link #login}, and finally do {@link #logout} or {@link #close} to 036 * remove it. 037 * <p> 038 * This is used for compatibility with previous code that expected to receive a {@link LoginContext} and then call 039 * {@link #logout} on it. 040 * 041 * @since 11.1 042 */ 043public class NuxeoLoginContext extends LoginContext implements AutoCloseable { 044 045 protected Principal principal; 046 047 protected Subject subject; 048 049 protected boolean loggedIn; 050 051 /** 052 * This constructor cannot be used, use the static method {@link #create} instead. 053 */ 054 public NuxeoLoginContext() throws LoginException { 055 super(error()); 056 } 057 058 private static String error() { 059 throw new UnsupportedOperationException("must use create() method"); 060 } 061 062 // constructs the class without calling its declared constructor or its super's constructor 063 // we need to do this because LoginContext construction is very costly 064 private static final Constructor<?> BARE_CONSTRUCTOR; 065 static { 066 try { 067 Class<?> klass = NuxeoLoginContext.class; 068 BARE_CONSTRUCTOR = ReflectionFactory.getReflectionFactory() 069 .newConstructorForSerialization(klass, 070 Object.class.getDeclaredConstructor()); 071 } catch (ReflectiveOperationException e) { 072 throw new ExceptionInInitializerError(e); 073 } 074 } 075 076 /** 077 * Creates a {@link NuxeoLoginContext} for the given principal. 078 * 079 * @param principal the principal 080 * @return the login context 081 */ 082 public static NuxeoLoginContext create(Principal principal) { 083 NuxeoLoginContext loginContext; 084 try { 085 loginContext = (NuxeoLoginContext) BARE_CONSTRUCTOR.newInstance(); 086 } catch (ReflectiveOperationException | IllegalArgumentException e) { 087 throw new RuntimeException(e); 088 } 089 loginContext.setPrincipal(principal); 090 return loginContext; 091 } 092 093 protected void setPrincipal(Principal principal) { 094 this.principal = principal; 095 this.subject = new Subject(true, Collections.singleton(principal), Collections.emptySet(), 096 Collections.emptySet()); 097 } 098 099 @Override 100 public Subject getSubject() { 101 return subject; 102 } 103 104 @Override 105 public void login() { 106 if (!loggedIn) { 107 LoginComponent.pushPrincipal(principal); 108 loggedIn = true; 109 } 110 } 111 112 @Override 113 public void logout() { 114 close(); 115 } 116 117 @Override 118 public void close() { 119 if (loggedIn) { 120 LoginComponent.popPrincipal(); 121 loggedIn = false; 122 } 123 } 124 125}