Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 8876c7cf043d6310119b540ed4c5caadc95d6dc8 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
/*******************************************************************************
 * Copyright (c) 2010 BSI Business Systems Integration AG.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors:
 *     BSI Business Systems Integration AG - initial API and implementation
 ******************************************************************************/
package org.eclipse.scout.rt.shared.security;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.security.BasicPermission;
import java.security.Permission;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.TreeSet;

import org.eclipse.scout.commons.TypeCastUtility;
import org.eclipse.scout.commons.exception.ProcessingException;
import org.eclipse.scout.commons.logger.IScoutLogger;
import org.eclipse.scout.commons.logger.ScoutLogManager;
import org.eclipse.scout.rt.shared.services.common.security.IAccessControlService;
import org.eclipse.scout.service.SERVICES;

public abstract class BasicHierarchyPermission extends BasicPermission {
  private static final long serialVersionUID = 1L;
  private static final IScoutLogger LOG = ScoutLogManager.getLogger(BasicHierarchyPermission.class);

  public static final int LEVEL_UNDEFINED = -1;
  public static final int LEVEL_NONE = 0;
  public static final int LEVEL_ALL = 100;
  private static long cacheTimeout = 60000L;

  public static long getCacheTimeoutMillis() {
    return cacheTimeout;
  }

  public static void setCacheTimeoutMillis(long t) {
    cacheTimeout = t;
  }

  private boolean m_readOnly;
  private int m_level;
  // cache
  private List<Integer> m_validLevels;

  public BasicHierarchyPermission(String name) {
    this(name, LEVEL_UNDEFINED);
  }

  public BasicHierarchyPermission(String name, int level) {
    super(name);
    buildLevelCache();
    setLevel(level);
  }

  @SuppressWarnings("boxing")
  private void buildLevelCache() {
    TreeSet<Integer> set = new TreeSet<Integer>();
    Field[] f = getClass().getFields();
    for (int i = 0; i < f.length; i++) {
      int flags = f[i].getModifiers();
      if (Modifier.isStatic(flags) && Modifier.isFinal(flags) && f[i].getName().startsWith("LEVEL_")) {
        try {
          int value = f[i].getInt(null);
          if (set.contains(value)) {
            throw new IllegalArgumentException("level " + f[i].getName() + " has the same value (" + value + ") as another level");
          }
          set.add(value);
        }
        catch (Exception e) {
          throw new IllegalArgumentException("could not build internal level cache", e);
        }
      }
    }
    m_validLevels = new ArrayList<Integer>(set);
  }

  /**
   * array of available levels, starting with the lowest, ending with the
   * highest
   */
  public final List<Integer> getValidLevels() {
    return Collections.unmodifiableList(m_validLevels);
  }

  public final int getLevel() {
    return m_level;
  }

  @SuppressWarnings("boxing")
  public final void setLevel(int level) {
    if (m_readOnly) {
      throw new SecurityException("Permission is read-only");
    }
    if (!m_validLevels.contains(level)) {
      throw new IllegalArgumentException("invalid level: " + level);
    }
    m_level = level;
  }

  public final void setReadOnly() {
    m_readOnly = true;
  }

  @Override
  public boolean equals(Object obj) {
    if (obj instanceof BasicHierarchyPermission) {
      BasicHierarchyPermission other = (BasicHierarchyPermission) obj;
      if (this.m_level == other.m_level) {
        if (super.equals(obj)) {
          return true;
        }
      }
    }
    return false;
  }

  @Override
  public int hashCode() {
    return super.hashCode() ^ m_level;
  }

  protected String getConfiguredId() {
    return null;
  }

  /**
   * when p.level has value {@link #LEVEL_UNDEFINED} and also {@link #calculateLevel(int)} returns #
   * {@link #LEVEL_UNDEFINED} then set
   * p.level to the maximum of its level
   */
  @Override
  public boolean implies(Permission p) {
    if (this.getClass().isAssignableFrom(p.getClass())) {
      BasicHierarchyPermission other = (BasicHierarchyPermission) p;
      if (super.implies(p)) {
        switch (this.m_level) {
          case LEVEL_ALL: {
            return true;
          }
          case LEVEL_UNDEFINED: {
            LOG.warn("The level of a " + this.getClass().getSimpleName() + " in the permission collection should not have the level LEVEL_UNDEFINED");
            return false;
          }
          case LEVEL_NONE: {
            return false;
          }
          default: {
            if (other.m_level == LEVEL_UNDEFINED) {
              if (checkLevel(other, this.m_level)) {
                return true;
              }
            }
            else {
              return this.m_level >= other.m_level;
            }
          }
        }
      }
    }
    return false;
  }

  @SuppressWarnings("boxing")
  private boolean checkLevel(BasicHierarchyPermission other, int level) {
    // check if we are in the backend
    if (SERVICES.getService(IAccessControlService.class).isProxyService()) {
      throw new FineGrainedAccessCheckRequiredException();
    }
    try {
      boolean b = other.execCheckLevel(level);
      return b;
    }
    catch (ProcessingException e) {
      throw new SecurityException(e);
    }
  }

  /**
   * Only called in the backend. Frontend uses proxy cache. Called by {@link #implies(Permission)} when level has value
   * #LEVEL_UNDEFINED
   * 
   * @param requiredLevel
   *          default implementation calls {@link #execCheckLevelData(int)} and
   *          returns true if data yields rows and first rows first value is 1
   */
  @SuppressWarnings("boxing")
  protected boolean execCheckLevel(int requiredLevel) throws ProcessingException {
    Object[][] data = execCheckLevelData(requiredLevel);
    return data != null && data.length > 0 && TypeCastUtility.castValue(data[0][0], Boolean.class);
  }

  /**
   * called by {@link #implies(Permission)} via execCheckLevel when level has
   * value #LEVEL_UNDEFINED
   * 
   * @param requiredLevel
   * @return data with data[0][0]=1 as true
   */
  protected Object[][] execCheckLevelData(int requiredLevel) throws ProcessingException {
    return null;
  }

}

Back to the top