Changeset 8045


Ignore:
Timestamp:
Jun 3, 2022, 8:59:50 AM (6 months ago)
Author:
Nicklas Nordborg
Message:

Merge BASE 3.19.3 to the trunk

Location:
trunk
Files:
15 edited

Legend:

Unmodified
Added
Removed
  • trunk

  • trunk/doc/src/docbook/developer/extensions.xml

    r7982 r8045  
    26862686      </sect3>
    26872687     
    2688       <sect3 id="pextensions_developer.login-manager.settings">
     2688      <sect3 id="extensions_developer.login-manager.settings">
    26892689        <title>Configuration settings</title>
    26902690     
     
    27472747        <para>
    27482748          All installed and enabled login forms are available in a selection list
    2749           from which the user can select to switch to another login form. For technical reasons
    2750           custom scripts and stylesheets are loaded for all installed login forms even
    2751           though only one is displayed at a time. Developers must ensure that CSS rules
    2752           and scripts are not affecting other login forms than the intended one.
     2749          from which the user can select to switch to another login form. Since BASE 3.19.3
     2750          custom scripts and stylesheets are only loaded for the currently active form.
     2751          In earlier BASE versions, custom scripts and stylesheets were loaded for all installed
     2752          login forms.
    27532753        </para>
    27542754        <para>
    2755           To help with this, BASE is setting two data-attributes on the
     2755          It is possible for both style sheets and scripts to verify that they are only used on
     2756          the intended login form. BASE is setting two data-attributes on the
    27562757          <sgmltag class="starttag">body</sgmltag> tag:
    27572758        </para>
     
    28162817      </note>
    28172818     
     2819      <sect3 id="extensions_developer.login-form.before-login">
     2820        <title>The before-login event</title>
     2821     
     2822        <para>
     2823          In BASE 3.19.3 the <code>before-login</code> was introduced. It is a custom event
     2824          that is sent to the <sgmltag class="starttag">form</sgmltag> tag just before the
     2825          login form is submitted to the server. Extensions may add event listeners for this
     2826          event if they need to take some action and they have the possibility to cancel
     2827          the submission by calling the <code>event.preventDefault()</code> method.
     2828        </para>
     2829       
     2830        <para>
     2831          This functionality is, for example, used by the <ulink
     2832          url="https://baseplugins.thep.lu.se/wiki/net.sf.basedb.webauthn">WebAuthn</ulink> extension,
     2833          which need to contact the server to get a <emphasis>challenge</emphasis>, ask the user
     2834          to insert and click on the security key, and then return the signed challenge in the
     2835          <code>extraField</code> before the login form is actually submitted. Since all of this
     2836          is handled asynchronously the <code>before-login</code> event need to be cancelled. The login
     2837          form can be submitted by calling <code>Login.submitLoginForm()</code>.
     2838        </para>
     2839      </sect3>
    28182840    </sect2>
    28192841   
  • trunk/src/clients/web/net/sf/basedb/clients/web/extensions/JspContext.java

    r6875 r8045  
    2323
    2424import java.util.Collection;
     25import java.util.HashMap;
    2526import java.util.HashSet;
     27import java.util.Map;
    2628import java.util.Set;
    2729
     
    8991  private final GuiContext guiContext;
    9092 
    91   private Set<String> scripts;
    92   private Set<String> stylesheets;
     93  private boolean needResourcesPerExtension;
     94  private Map<String, Set<String>> scripts;
     95  private Map<String, Set<String>> stylesheets;
    9396 
    9497  JspContext(SessionControl sc, DbControl dc,
     
    100103  }
    101104
     105  /**
     106    Set a flag indicating that resources (eg. scripts and stylesheets)
     107    should be tracked per extension. If this is set, it is possible to
     108    use {@link #getScripts(String)} and {@link #getStylesheets(String)}
     109    to get the resources that was added by the given extension.
     110    @since 3.19.3
     111  */
     112  public void setNeedResourcesPerExtension(boolean nrpe)
     113  {
     114    this.needResourcesPerExtension = nrpe;
     115  }
     116 
    102117  /**
    103118    Get the JSP Page context object for the current request.
     
    176191  public void addScript(String absolutePath)
    177192  {
    178     if (scripts == null) scripts = new HashSet<String>();
    179     scripts.add(absolutePath);
     193    if (scripts == null) scripts = new HashMap<>();
     194    getSet(scripts, null).add(absolutePath);
     195    if (needResourcesPerExtension)
     196    {
     197      getSet(scripts, getCurrentExtension().getId()).add(absolutePath);
     198    }
     199  }
     200 
     201  /**
     202    Get the Set that is stored under the given key. If
     203    no Set exists it is created and stored in the map.
     204  */
     205  private Set<String> getSet(Map<String, Set<String>> map, String key)
     206  {
     207    Set<String> set = map.get(key);
     208    if (set == null)
     209    {
     210      set = new HashSet<>();
     211      map.put(key, set);
     212    }
     213    return set;
    180214  }
    181215 
     
    200234  public void addStylesheet(String absolutePath)
    201235  {
    202     if (stylesheets == null) stylesheets = new HashSet<String>();
    203     stylesheets.add(absolutePath);
     236    if (stylesheets == null) stylesheets = new HashMap<>();
     237    getSet(stylesheets, null).add(absolutePath);
     238    if (needResourcesPerExtension)
     239    {
     240      getSet(stylesheets, getCurrentExtension().getId()).add(absolutePath);
     241    }
    204242  }
    205243
     
    210248  public Collection<String> getScripts()
    211249  {
    212     return scripts;
     250    return scripts == null ? null : scripts.get(null);
     251  }
     252 
     253  /**
     254    Get all scripts that has been added to this context
     255    by the given extension
     256    @return A collection of scripts
     257    @since 3.19.3
     258  */
     259  public Collection<String> getScripts(String xtId)
     260  {
     261    return scripts == null ? null : scripts.get(xtId);
    213262  }
    214263 
     
    219268  public Collection<String> getStylesheets()
    220269  {
    221     return stylesheets;
     270    return stylesheets == null ? null : stylesheets.get(null);
     271  }
     272 
     273  /**
     274    Get all stylesheets that has been added to this context
     275    by the given extension.
     276    @return A collection of stylesheets
     277    @since 3.19.3
     278  */
     279  public Collection<String> getStylesheets(String xtId)
     280  {
     281    return stylesheets == null ? null : stylesheets.get(xtId);
    222282  }
    223283 
  • trunk/src/clients/web/net/sf/basedb/clients/web/extensions/login/FieldInfo.java

    r6426 r8045  
    2222package net.sf.basedb.clients.web.extensions.login;
    2323
     24import net.sf.basedb.clients.web.extensions.DynamicActionAttributeSupport;
     25
    2426/**
    2527  Information about a the login/password fields on the login form.
     
    2931*/
    3032public class FieldInfo
     33  extends DynamicActionAttributeSupport
    3134{
    3235
  • trunk/src/clients/web/net/sf/basedb/clients/web/extensions/login/LoginFormBean.java

    r7529 r8045  
    2222package net.sf.basedb.clients.web.extensions.login;
    2323
     24import net.sf.basedb.clients.web.extensions.DynamicActionAttributeSupport;
     25
    2426/**
    2527  A simple implementation of the {@link LoginFormAction} interface.
     
    2931*/
    3032public class LoginFormBean
     33  extends DynamicActionAttributeSupport
    3134  implements LoginFormAction
    3235{
  • trunk/src/clients/web/net/sf/basedb/clients/web/taglib/extensions/Scripts.java

    r7703 r8045  
    6363    </td>
    6464  </tr>
     65  <tr>
     66    <td>extension</td>
     67    <td>-</td>
     68    <td>no</td>
     69    <td>
     70      If set, only scripts defined by the given extension are
     71      included in the output.
     72    </td>
     73  </tr>
    6574  </table>
    6675
     
    7685  // The JSP context
    7786  private JspContext context = null;
     87  // If set, only output scripts defined by the given extension
     88  private String extensionId;
    7889
    7990  public void setContext(JspContext context)
    8091  {
    8192    this.context = context;
     93  }
     94 
     95  /**
     96    @since 3.19.3
     97  */
     98  public void setExtension(String extensionId)
     99  {
     100    this.extensionId = extensionId;
    82101  }
    83102
     
    88107    if (context == null) return SKIP_BODY;
    89108
    90     Collection<String> scripts = context.getScripts();
     109    Collection<String> scripts = context.getScripts(extensionId);
    91110    if (scripts == null) return SKIP_BODY;
    92111   
  • trunk/src/clients/web/net/sf/basedb/clients/web/taglib/extensions/Stylesheets.java

    r7703 r8045  
    6464    </td>
    6565  </tr>
     66  <tr>
     67    <td>extension</td>
     68    <td>-</td>
     69    <td>no</td>
     70    <td>
     71      If set, only scripts defined by the given extension are
     72      included in the output.
     73    </td>
     74  </tr>
    6675  </table>
    6776
     
    7786  // The JSP context
    7887  private JspContext context = null;
     88  // If set, only output scripts defined by the given extension
     89  private String extensionId;
    7990
    8091  public void setContext(JspContext context)
    8192  {
    8293    this.context = context;
     94  }
     95 
     96  /**
     97    @since 3.19.3
     98  */
     99  public void setExtension(String extensionId)
     100  {
     101    this.extensionId = extensionId;
    83102  }
    84103
     
    89108    if (context == null) return SKIP_BODY;
    90109
    91     Collection<String> stylesheets = context.getStylesheets();
     110    Collection<String> stylesheets = context.getStylesheets(extensionId);
    92111    if (stylesheets == null) return SKIP_BODY;
    93112   
  • trunk/src/core/net/sf/basedb/core/SessionControl.java

    r7962 r8045  
    143143  */
    144144  private boolean closed;
     145
     146  /**
     147    Stores name and value of session settings.
     148    @since 3.19.3
     149  */
     150  private Map<String,Object> sessionSettings;
    145151 
    146152  /**
     
    188194    this.currentClient = parent.currentClient;
    189195    this.remoteId = parent.remoteId;
     196    this.sessionSettings = parent.sessionSettings;
    190197    this.dbControlCache = Collections.synchronizedMap(new WeakHashMap<DbControl,String>());
    191198    this.currentContexts = parent.currentContexts;
     
    219226    this.id = id;
    220227    this.remoteId = remoteId;
     228    this.sessionSettings = Collections.synchronizedMap(new HashMap<String,Object>());
    221229    this.dbControlCache = Collections.synchronizedMap(new WeakHashMap<DbControl,String>());
    222230    this.currentContexts = Collections.synchronizedMap(new HashMap<ContextKey, ItemContext>());
     
    568576    updateLastAccess();
    569577    // Are we already logged in?
    570     if (isLoggedIn())
    571     {
    572       throw new AlreadyLoggedInException(loginInfo.userLogin);
    573     }
    574578    if (loginRequest == null) throw new InvalidUseOfNullException("loginRequest");
    575579    if (loginRequest.getLogin() == null) throw new InvalidUseOfNullException("loginRequest.login");
     580    if (isLoggedIn() && !loginRequest.isVerifyOnly())
     581    {
     582      throw new AlreadyLoggedInException(loginInfo.userLogin);
     583    }
    576584
    577585    org.hibernate.Session session = null;
     
    609617        throw new LoginException(ex.getMessage(), ex);
    610618      }
     619     
     620      // Return now if the login request is for verification only
     621      if (loginRequest.isVerifyOnly()) return;
    611622     
    612623      // The login was ok so far... check device verification
     
    12371248    li.userName = userData.getName();
    12381249    li.authenticationMethod = authenticationMethod;
    1239     li.sessionSettings = Collections.synchronizedMap(new HashMap<String,Object>());
    12401250    return li;
    12411251  }
     
    25932603  public <T> T getSessionSetting(String name)
    25942604  {
    2595     return loginInfo == null ? null : (T)loginInfo.sessionSettings.get(name);
     2605    return (T)sessionSettings.get(name);
    25962606  }
    25972607
     
    26072617  public <T> T setSessionSetting(String name, Object value)
    26082618  {
    2609     if (loginInfo == null || loginInfo.sessionSettings == null) return null;
    26102619    if (value == null)
    26112620    {
    2612       return (T)loginInfo.sessionSettings.remove(name);
     2621      return (T)sessionSettings.remove(name);
    26132622    }
    26142623    else
    26152624    {
    2616       return (T)loginInfo.sessionSettings.put(name, value);
     2625      return (T)sessionSettings.put(name, value);
    26172626    }
    26182627  }
     
    29382947    private Map<String,SettingInfo> userDefaultSettings;
    29392948
    2940     /**
    2941       Stores name and value of session settings.
    2942     */
    2943     private Map<String,Object> sessionSettings;
    2944 
    29452949    private LoginInfo()
    29462950    {}
     
    29612965      this.userClientSettings = parent.userClientSettings;
    29622966      this.userDefaultSettings = parent.userDefaultSettings;
    2963       this.sessionSettings = parent.sessionSettings;
    29642967    }
    29652968   
  • trunk/src/core/net/sf/basedb/core/authentication/LoginRequest.java

    r7408 r8045  
    2424import java.util.HashMap;
    2525import java.util.Map;
     26
     27import net.sf.basedb.core.SessionControl;
    2628
    2729/**
     
    3436public class LoginRequest
    3537{
    36 
     38  private boolean verifyOnly;
    3739  private int userId;
    3840  private String login;
     
    8688  }
    8789
     90  /**
     91    Set a flag to indicate if the login request should only verify
     92    the login parameters and not do a full login.
     93    @since 3.19.3
     94  */
     95  public void setVerifyOnly(boolean verifyOnly)
     96  {
     97    this.verifyOnly = verifyOnly;
     98  }
     99 
     100  /**
     101    If this flag is set, the login parameters are only verified and
     102    the actual login is aborted. If the verification is successful, the
     103    {@link SessionControl#login(LoginRequest)} returns normally, otherwise
     104    an exception is thrown.
     105    @since 3.19.3
     106  */
     107  public boolean isVerifyOnly()
     108  {
     109    return verifyOnly;
     110  }
    88111 
    89112  /**
  • trunk/src/core/net/sf/basedb/util/extensions/ClientContext.java

    r7895 r8045  
    6969  private final SessionControl sc;
    7070  private final DbControl dc;
     71  private ExtensionPoint<?> currentXtPoint;
     72  private Extension<?> currentXt;
    7173  private Object item;
    7274  private Map<String, Object> attributes;
     
    173175  {
    174176    this.item = item;
     177  }
     178 
     179  /**
     180    Get the currently active extension point.
     181    @since 3.19.3
     182  */
     183  public ExtensionPoint<?> getCurrentExtensionPoint()
     184  {
     185    return currentXtPoint;
     186  }
     187  protected void setCurrentExtensionPoint(ExtensionPoint<?> xtPoint)
     188  {
     189    this.currentXtPoint = xtPoint;
     190  }
     191 
     192  /**
     193    Get the currently active extension point.
     194    @since 3.19.3
     195  */
     196  public Extension<?> getCurrentExtension()
     197  {
     198    return currentXt;
     199  }
     200  protected void setCurrentExtension(Extension<?> xt)
     201  {
     202    this.currentXt = xt;
    175203  }
    176204 
  • trunk/src/core/net/sf/basedb/util/extensions/Registry.java

    r7642 r8045  
    673673
    674674      // YES! ...
     675      if (clientContext != null) clientContext.setCurrentExtensionPoint(rep);
    675676      ErrorHandlerFactory ehf = rep.getErrorHandlerFactory();
    676677      ExtensionPointContext<Action> mainContext = new ExtensionPointContext<Action>(this,
     
    690691
    691692        // Create invokation context for the extension
     693        if (clientContext != null) clientContext.setCurrentExtension(ext);
    692694        ExtensionContext<A> context =
    693695          new ExtensionContext(mainContext, ext);
  • trunk/www/WEB-INF/extensions.tld

    r7604 r8045  
    4141      <rtexprvalue>true</rtexprvalue>
    4242    </attribute>
     43    <attribute>
     44      <name>extension</name>
     45      <required>false</required>
     46      <rtexprvalue>true</rtexprvalue>
     47    </attribute>
    4348  </tag>
    4449
     
    5055      <name>context</name>
    5156      <required>true</required>
     57      <rtexprvalue>true</rtexprvalue>
     58    </attribute>
     59    <attribute>
     60      <name>extension</name>
     61      <required>false</required>
    5262      <rtexprvalue>true</rtexprvalue>
    5363    </attribute>
  • trunk/www/login.js

    r7815 r8045  
    305305      }
    306306    }
     307   
     308    // Send custom event to let extensions do stuff before submitting
     309    Doc.hide('login-error');
     310    var evt = new CustomEvent('before-login', { cancelable: true, bubbles: true });
     311    if (frm.dispatchEvent(evt))
     312    {
     313      login.submitLoginForm();
     314    }
     315  }
     316 
     317  /**
     318    Submit the login form with no questions asked.
     319  */
     320  login.submitLoginForm = function()
     321  {
     322    var frm = document.forms['login'];
    307323    if (!pUseLastLogin)
    308324    {
     
    317333      frm.submit();
    318334    }
    319    
    320335  }
    321336 
  • trunk/www/main.jsp

    r7954 r8045  
    5252  import="net.sf.basedb.clients.web.extensions.login.FieldInfo"
    5353  import="net.sf.basedb.clients.web.extensions.login.PasswordLoginFormFactory"
     54  import="net.sf.basedb.clients.web.extensions.DynamicActionAttributeSupport"
    5455  import="net.sf.basedb.util.Values"
    5556  import="java.util.Date"
     
    8788  ItemResultIterator<News> news = null;
    8889  JspContext jspContext = ExtensionsControl.createContext(dc, pageContext);
     90  jspContext.setNeedResourcesPerExtension(true);
    8991  ExtensionsInvoker<LoginFormAction> invoker = ExtensionsControl.useExtensions(jspContext, "net.sf.basedb.clients.web.login-form");
    9092
    9193  LoginFormAction loginAction = null;
    9294  String selectedLoginForm = null;
     95  String selectedExtension = null;
    9396  Map<String, String> allForms = new TreeMap<String, String>();
    9497 
     
    109112        loginAction = action;
    110113        selectedLoginForm = formId;
     114        selectedExtension = it.getExtension().getId();
    111115      }
    112116    }
     
    124128  <base:page type="default">
    125129  <base:head styles="login.css" scripts="~login.js">
    126     <ext:scripts context="<%=jspContext%>" />
    127     <ext:stylesheets context="<%=jspContext%>" />
     130    <ext:scripts context="<%=jspContext%>" extension="<%=selectedExtension%>" />
     131    <ext:stylesheets context="<%=jspContext%>" extension="<%=selectedExtension%>" />
    128132  </base:head>
    129133  <base:body style="padding-top: 5em;" data-login-form="<%=HTML.encodeTags(selectedLoginForm)%>" data-requested-form="<%=HTML.encodeTags(requestedLoginForm) %>">
    130     <form name="login" action="login.jsp" method="post">
     134    <form name="login" action="login.jsp" method="post"
     135      <%=DynamicActionAttributeSupport.getAttributesString(loginAction)%>
     136    >
    131137    <input type="hidden" name="ID" value="<%=ID%>">
    132138    <input type="hidden" name="again" value="<%=again?1:0%>">
     
    171177        <%
    172178      }
    173       if (error != null)
    174       {
    175         %>
    176         <div class="messagecontainer error" style="margin-top: 1em; margin-bottom: 1em;"><%=error%></div>
    177         <%
    178       }
    179179      %>
     180      <div id="login-error" class="messagecontainer error"
     181        style="margin-top: 1em; margin-bottom: 1em;<%=error==null?"display:none;":""%>">
     182        <%=error==null?"":error%>
     183      </div>
    180184      <table style="width: 100%; border-collapse: separate;">
    181185      <tr>
     
    195199                <%=loginAction.rememberLastLogin() ? "" : "autocomplete=\"off\" data-use-last-login=\"0\""%>
    196200                maxlength="100"
    197                 tabindex="1">
     201                tabindex="1"
     202                <%=DynamicActionAttributeSupport.getAttributesString(loginField)%>
     203                >
    198204              </td>
    199205            </tr>
     
    207213                <%=valueIfNotNull("placeholder=\"", passwordField.getPlaceHolder(), "\"") %>
    208214                maxlength="80"
    209                 tabindex="2">
     215                tabindex="2"
     216                <%=DynamicActionAttributeSupport.getAttributesString(passwordField)%>
     217                >
    210218              </td>
    211219              <td <%=extraField != null?"rowspan=\"2\"" : "" %> style="vertical-align: bottom;">
     
    237245                  <%=valueIfNotNull("placeholder=\"", extraField.getPlaceHolder(), "\"") %>
    238246                  maxlength="80"
    239                   tabindex="3">
     247                  tabindex="3"
     248                  <%=DynamicActionAttributeSupport.getAttributesString(extraField)%>
     249                  >
    240250                </td>
    241251              </tr>
  • trunk/www/switch.jsp

    r7954 r8045  
    3838  import="net.sf.basedb.clients.web.extensions.ExtensionsControl"
    3939  import="net.sf.basedb.clients.web.extensions.JspContext"
     40  import="net.sf.basedb.clients.web.extensions.DynamicActionAttributeSupport"
    4041  import="net.sf.basedb.clients.web.extensions.login.LoginFormAction"
    4142  import="net.sf.basedb.clients.web.extensions.login.PasswordLoginFormFactory"
     
    6465{
    6566  JspContext jspContext = ExtensionsControl.createContext(dc, pageContext);
     67  jspContext.setNeedResourcesPerExtension(true);
    6668  ExtensionsInvoker<LoginFormAction> invoker = ExtensionsControl.useExtensions(jspContext, "net.sf.basedb.clients.web.login-form");
    6769
    6870  LoginFormAction loginAction = null;
    6971  String selectedLoginForm = null;
     72  String selectedExtension = null;
    7073  Map<String, String> allForms = new TreeMap<String, String>();
    7174
     
    8689        loginAction = action;
    8790        selectedLoginForm = formId;
     91        selectedExtension = it.getExtension().getId();
    8892      }
    8993    }
     
    100104  <base:page type="popup" title="Switch user">
    101105  <base:head styles="login.css" scripts="~login.js">
    102     <ext:scripts context="<%=jspContext%>" />
    103     <ext:stylesheets context="<%=jspContext%>" />
     106    <ext:scripts context="<%=jspContext%>" extension="<%=selectedExtension%>" />
     107    <ext:stylesheets context="<%=jspContext%>" extension="<%=selectedExtension%>" />
    104108  </base:head>
    105109  <base:body data-login-form="<%=HTML.encodeTags(selectedLoginForm)%>" data-requested-form="<%=HTML.encodeTags(requestedLoginForm) %>">
    106110    <h1>Switch user <base:help helpid="switchuser" /></h1>
    107     <form name="login" action="login.jsp" method="post">
     111    <form name="login" action="login.jsp" method="post"
     112      <%=DynamicActionAttributeSupport.getAttributesString(loginAction)%>
     113    >
    108114    <input type="hidden" name="ID" value="<%=ID%>">
    109115    <input type="hidden" name="again" value="1">
     
    148154          data-use-last-login="0"
    149155          maxlength="100"
    150           tabindex="0">
     156          tabindex="0"
     157          <%=DynamicActionAttributeSupport.getAttributesString(loginField)%>
     158          >
    151159        </td>
    152160      </tr>
     
    159167          <%=valueIfNotNull("placeholder=\"", passwordField.getPlaceHolder(), "\"") %>
    160168          maxlength="80"
    161           tabindex="0">
     169          tabindex="0"
     170          <%=DynamicActionAttributeSupport.getAttributesString(passwordField)%>
     171          >
    162172        </td>
    163173      </tr>
     
    174184            <%=valueIfNotNull("placeholder=\"", extraField.getPlaceHolder(), "\"") %>
    175185            maxlength="80"
    176             tabindex="0">
     186            tabindex="0"
     187            <%=DynamicActionAttributeSupport.getAttributesString(extraField)%>
     188            >
    177189          </td>
    178190        </tr>
     
    194206            <%=loginAction.getHelp() %>
    195207            </div>
     208            <div id="login-error" class="messagecontainer error"
     209              style="margin-top: 1em; margin-bottom: 1em; display:none;">
     210            </div>
    196211          </div>
    197212          </td>
     
    204219        <tr class="dynamic">
    205220          <th></th>
    206           <td></td>
     221          <td>
     222            <div id="login-error" class="messagecontainer error"
     223              style="margin-top: 1em; margin-bottom: 1em; display:none;">
     224            </div>
     225          </td>
    207226        </tr>
    208227        <%
Note: See TracChangeset for help on using the changeset viewer.