I followed this guide as a template to creating my custom authentication provider
DotNetNuke Tips and Tricks #12: Creating your own Authentication Provider
In that guide he creates his own authentication configuration class so that it does not share configuration with the default provider (which he built his custom provider off) which i converted to c#.
[Serializable()]
public class ScanToLoginAuthConfig
{
#region "Private Members"
private bool _Enabled = true;
private int _portalId;
private const string CACHEKEY = "Authentication.ScanToLogin.DNN";
private const string ENABLED_SETTING_KEY = "DNN_ScanToLogin_Enabled";
#endregion
#region "Constructor(s)"
protected ScanToLoginAuthConfig(int portalID)
{
_portalId = portalID;
try
{
string setting = Null.NullString;
if (PortalController.GetPortalSettingsDictionary(portalID)
.TryGetValue(ENABLED_SETTING_KEY, out setting))
{
_Enabled = bool.Parse(setting);
}
setting = Null.NullString;
}
catch
{
}
}
#endregion
#region "Public Properties"
public bool Enabled
{
get { return _Enabled; }
set { _Enabled = value; }
}
[Browsable(false)]
public int PortalId
{
get { return _portalId; }
set { _portalId = value; }
}
#endregion
#region "Public SHared Methods"
public static void ClearConfig(int portalId)
{
string key = CACHEKEY + "_" + portalId.ToString();
DataCache.RemoveCache(key);
}
public static ScanToLoginAuthConfig GetConfig(int portalId)
{
string key = CACHEKEY + "_" + portalId.ToString();
ScanToLoginAuthConfig config = (ScanToLoginAuthConfig) DataCache.GetCache(key);
if (config == null)
{
config = new ScanToLoginAuthConfig(portalId);
DataCache.SetCache(key, config);
}
return config;
}
public static void UpdateConfig(ScanToLoginAuthConfig config)
{
PortalController.UpdatePortalSetting(config.PortalId, "DNN_Enabled", config.Enabled.ToString());
ClearConfig(config.PortalId);
}
#endregion
}
This works in so far as I con toggle my authentication provider as enabled or disabled independently of the default provider, however, my provider does not load if the default provider is not enabled.
if (PortalController.GetPortalSettingsDictionary(portalID)
.TryGetValue(ENABLED_SETTING_KEY, out setting))
{
_Enabled = bool.Parse(setting);
}
When I go in to modify the settings the above code fires, but it never finds the value specified by "ENABLED_SETTING_KEY" ("DNN_ScanToLogin_Enabled"). The portal settings dictionary (for that portal ID) contains 14 items and "DNN_ScanToLogin_Enabled" is not in there. Can anyone tell me why that is, PLEASE!
EDIT
I've modified things somewhat
public static void ClearConfig(int portalId)
{
string key = CACHEKEY + "_" + portalId.ToString();
DataCache.RemoveCache(key);
}
and
public static ScanToLoginAuthConfig GetConfig(int portalId)
{
string key = CACHEKEY + "_" + portalId.ToString();
Have become
public static void UpdateConfig(ScanToLoginAuthConfig config)
{
PortalController.UpdatePortalSetting(config.PortalId, "DNN_ScanToLogin_Enabled", config.Enabled.ToString());
ClearConfig(config.PortalId);
}
and
public static ScanToLoginAuthConfig GetConfig(int portalId)
{
string key = CACHEKEY;// +"_" + portalId.ToString();
Respectively. So now the keys seem to update and set correctly (for my unspecified definition of correctly...)
However when I disable the default authentication provider and enable my authentication provider, it still does not display my provider and instead shows the default. When both default and mine are enabled, then both display, tabbed.
I also updated the following to include the key's name as it is defined at the top of the code.
public static void UpdateConfig(ScanToLoginAuthConfig config)
{
PortalController.UpdatePortalSetting(config.PortalId, "DNN_ScanToLogin_Enabled", config.Enabled.ToString());
ClearConfig(config.PortalId);
}
The first thing I would confirm is that when you installed your Authentication Provider under Host > Extensions, you configured the Enabled flag there.
BUT, to enable it for a particular Portal, you need to go to Admin > Extensions, open the Authentication Systems section, click Edit on your Provider, and Enable it there.
If you still have problems, you may issues with your settings implementation. (If you implemented settings using the AuthenticationSettingsBase).
you must change the code inside your login file and use your ScanToLoginAuthConfig class for Enabled property
public override bool Enabled
{
get
{
return ScanToLoginAuthConfig.GetConfig(PortalId).Enabled;
}
}
Related
I have created a project using abpframwork. When running swagger, swagger receives the function in the application layer is a api. I don't want that. Can you guys tell me how to remove it in swagger
Code in Application Layer
public class UserService : AdminSSOAppService, ITransientDependency, IValidationEnabled, IUserService
{
IUserRepository _userRepository;
private readonly ILogger<UserService> _log;
public UserService(IUserRepository userRepository,
ILogger<UserService> log
)
{
_userRepository = userRepository;
_log = log;
}
public async Task<List<UserDto>> GetList()
{
var list = await _userRepository.GetListAsync();
return ObjectMapper.Map<List<User>, List<UserDto>>(list);
}
public async Task<UserDto> GetUserById(int Id)
{
var user = await _userRepository.GetAsync(c=>c.Id == Id);
return ObjectMapper.Map<User, UserDto>(user);
}
}
Code in HttpApi Layer
[Area(AdminSSORemoteServiceConsts.ModuleName)]
[RemoteService(Name = AdminSSORemoteServiceConsts.RemoteServiceName)]
[Route("api/user/user-profile")]
public class UserController : ControllerBase, IUserService
{
private readonly IUserService _userAppService;
public UserController(IUserService userAppService)
{
_userAppService = userAppService;
}
[HttpGet]
[Route("get-list-httpapi")]
public Task<List<UserDto>> GetList()
{
return _userAppService.GetList();
}
[HttpGet]
[Route("get-by-id-httpapi")]
public Task<UserDto> GetUserById(int Id)
{
return _userAppService.GetUserById(Id);
}
}
I can suggest a workaround as to enable only the APIs you need to appear on swagger (though the ones that don't appear anymore will still be available for consumption).
I would suggest you add a configuration part in your *.Http.Api project module inside your ConfigureSwaggerServices, like so:
context.Services.AddSwaggerGen(options =>
{
options.DocInclusionPredicate(
(_, apiDesc) =>
apiDesc
.CustomAttributes()
.OfType<IncludeInSwaggerDocAttribute>()
.Any());
});
And for the attribute, it would be very simple, like so:
[AttributeUsage(AttributeTargets.Class)]
public class IncludeInSwaggerDocAttribute : Attribute
{
}
This will let you achieve what you want, however I still recommend reading the doc carefully to be able to implement DDD.
I have a standard Hoemcontroller in ASP.NET Core MVC:
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
public HomeController(ILogger<HomeController> logger)
{
_logger = logger;
}
public IActionResult Index(string user)
{
if(user != null)
{
TempData["UserName"] = user;
return View("Index", user);
}
return View("Index");
}
public IActionResult Privacy()
{
return View();
}
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
}
In the Index Action there will be a parameter sent from a winforms application. The string will contain the username of the client connecting to the website. This is the code for winforms:
public partial class Form1 : Form
{
public ChromiumWebBrowser chromeBrowser;
public Form1()
{
InitializeComponent();
InitializeChromium();
this.WindowState = FormWindowState.Maximized;
}
public void InitializeChromium()
{
CefSettings settings = new CefSettings();
Cef.Initialize(settings);
Cef.EnableHighDPISupport();
chromeBrowser = new ChromiumWebBrowser("https://localhost:5001/Home/Index/" + Environment.UserName);
this.Controls.Add(chromeBrowser);
//chromeBrowser.Dock = DockStyle.Fill;
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
Cef.Shutdown();
}
}
Now I am unsure on how to handle this on the webserver. I get the username inside the HomeController but in the same time when a user goes to the other pages with their controllers then the user should see only his content. Is that even possible?
It is not good practice to do authorization through a parameter in URI. For that, you should use Authentication (when the user passes his login and password) and receive a token with permissions. After that, you pass the token to the server and check permission there(using Authorize attribute for example). Example
If you are making a test project, and you don't need authentication at all, then you can pass a username everywhere you need and write some code to handle the content of every user (using headers, URI parameters, etc.)
Currently I am working designing my project in Specflow. I want to implement some reporting to my project. Currently I have created one separate .cs file and kept all my report setting. But these steps are getting unreachable. Can anyone please guide me how i can design my flow and how i can integrate with the feature file?
Please find the below BaseReport.cs file and my Step definition file.
namespace Verivox.CommonLib
{
public class BaseReport
{
public static ExtentReports extent;
public static ExtentTest test;
[BeforeFeature()]
public static void BasicSetUp()
{
//string pth = System.Reflection.Assembly.GetCallingAssembly().CodeBase;
string pth = System.IO.Directory.GetCurrentDirectory();
string actualPath = pth.Substring(0, pth.LastIndexOf("bin"));
string projectPath = new Uri(actualPath).LocalPath;
string reportPath = projectPath + "Reports\\" + FeatureContext.Current.FeatureInfo.Title + ".html";
extent = new ExtentReports(reportPath, true);
extent.LoadConfig(projectPath + "CommonLib\\Extent-config.xml");
}
[BeforeScenario()]
public static void BeforeScenarioSetUp()
{
test = extent.StartTest("Running Scenario -->" + ScenarioContext.Current.ScenarioInfo.Title);
}
[AfterScenario()]
public static void AfterScnario()
{
if (ScenarioContext.Current.TestError != null)
{
var error = ScenarioContext.Current.TestError;
var errormessage = "<pre>" + error.Message + "</pre>";
//Add capture screen shot line here
extent.EndTest(test);
}
}
[AfterFeature()]
public static void EndReport()
{
extent.Flush();
// extent.Close();
}
}
}
Steps
namespace Verivox.Steps
{
[Binding]
class CalculationVerificationSteps
{
[Given(#"I have navigate to Home Page")]
public void GivenIHaveNavigateToHomePage()
{
Browser.Current.Navigate().GoToUrl(ConfigurationManager.AppSettings["seleniumBaseUrl"]);
PropertyCollection.currentPage = new HomePage();
Browser.Current.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(10));
}
[Given(#"Navigate to Mobile Calculator under All Comparison Section")]
public void GivenNavigateToMobileCalculatorUnderAllComparisonSection()
{
PropertyCollection.currentPage.As<HomePage>().MainCompItemClick("Telekommunikation");
PropertyCollection.currentPage.As<HomePage>().SubCompItemClick("Mobilfunk");
PropertyCollection.currentPage.As<HomePage>().CalculatorLinkClick("Mobiles Internet");
}
[Then(#"Mobile Calculator should appear")]
public void ThenMobileCalculatorShouldAppear()
{
Assert.IsTrue(PropertyCollection.currentPage.As<HomePage>().IsMobileInternetCalcExistance());
}
[Then(#"(.*) option and (.*) option is selected by default\.")]
public void ThenMonatsflatrateOptionAndSIMOptionIsSelectedByDefault_(string defaultTarif, string hardware)
{
try
{
Assert.IsTrue(PropertyCollection.currentPage.As<HomePage>().VerifyMobiIntSelectedItem(defaultTarif));
string colorCode = PropertyCollection.currentPage.As<HomePage>().VerifySelectedHardWare();
Assert.AreEqual(0, string.Compare("rgba(253, 138, 2, 1)", colorCode, StringComparison.OrdinalIgnoreCase));
}
catch (Exception)
{
BaseReport.test.Log(LogStatus.Fail, "Default selections are incorrect.");
}
}
You are missing the Binding- attribute on the BaseReport class. Without that, the hooks defined there are not called.
I am new to ASP.NET MVC 5 and so I am trying to use it as much as possible to learn it by practice.
So I am thinking of using the new OWIN implementation of ASP.NET MVC to implement the authentication and authorization of my project. That said, I am building the project in a way that it can work with various types of databases.
So far I have used generic ADO.NET elements (e.g. DbDataReader etc) and I have refused to use any ORM. So I am wondering if I can go ahead with using the new identity system of ASP.NET or will I be bound to Entity Framework and SQL Server if I do so?
Not that simple. Not that hard either.
You'll have to write your custom implementation of:
IUserStore<TUser>
IUserPasswordStore<TUser>
IUserTwoFactorStore<TUser>
IUserClaimStore<TUser>
IRoleStore<TRole>
IUserSecurityStampStore<TUser, string>
IUserRoleStore<TUser, string>
UserManager<TUser>
Then create your own user implementation, from IUser<TKey>, like:
public class MyUser : IUser<string>
{
public string Id { get; set; }
public string UserName { get; set; }
}
Finally, from NuGet, remove AspNet.Identity.EntityFramework, which will remove EntityFramework too if you're not using it elsewhere.
Wherever your code breaks, rewrite it to use your custom implementations.
Tip
Create a MyUserRepository which implements items from 1 to 7.
Then, create a MyUserManager which implements item 8.
It will be damn easy to wire that up in place of default AspNet.Identity.EntityFramework classes.
To piggy-back on what ALMMa said, when I was working on my own custom implementation, I found this article to be invaluable:
Overview of Custom Storage Providers for ASP.NET Identity
It details no only what Interfaces need to be implemented, but goes into detail on how to implement them and gives code sample references to an actual MySQL implementation.
You just need to override some classes in the following manner to get basic role based authentication working without Entity Framework and SQL..
public partial class Startup
{
public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; }
public void ConfigureAuth(IAppBuilder app)
{
// Configure the db context, user manager and role manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
// Enable the application to use a cookie to store information for the signed in user
// and to use a cookie to temporarily store information about a user logging in with a third party login provider
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
// Configure the application for OAuth based flow
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new ApplicationOAuthProvider(),
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
// In production mode set AllowInsecureHttp = false
AllowInsecureHttp = true
};
// Enable the application to use bearer tokens to authenticate users
app.UseOAuthBearerTokens(OAuthOptions);
}
}
public class ApplicationUser : IUser
{
public ApplicationUser()
{
Id = Guid.NewGuid().ToString();
Roles = new List<string>();
}
public virtual string Email { get; set; }
public List<string> Roles { get; set; }
public virtual string Password { get; set; }
public DateTime CreatedTime { get; set; }
public DateTime UpdatedTime { get; set; }
public string Id { get; }
public string UserName { get; set; }
public virtual void AddRole(string role)
{
Roles.Add(role);
}
public virtual void RemoveRole(string role)
{
Roles.Remove(role);
}
}
public class ApplicationUserManager : UserManager<ApplicationUser>
{
public ApplicationUserManager(IUserStore<ApplicationUser> store)
: base(store)
{
}
public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options,
IOwinContext context)
{
var manager =
new ApplicationUserManager(
new UserStoreService<ApplicationUser>(context.Get<ApplicationDbContext>().Users));
manager.PasswordHasher = new FusionPasswordHasher();
// Configure validation logic for passwords
manager.PasswordValidator = new PasswordValidator
{
RequiredLength = 6,
RequireNonLetterOrDigit = false,
RequireDigit = false,
RequireLowercase = false,
RequireUppercase = false
};
// Configure user lockout defaults
manager.UserLockoutEnabledByDefault = true;
manager.DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(5);
manager.MaxFailedAccessAttemptsBeforeLockout = 5;
var dataProtectionProvider = options.DataProtectionProvider;
if (dataProtectionProvider != null)
manager.UserTokenProvider =
new DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("ASP.NET Identity"));
return manager;
}
public virtual async Task<IdentityResult> AddUserToRolesAsync(string userId, IList<string> roles)
{
var userRoleStore = (IUserRoleStore<ApplicationUser, string>) Store;
var user = await FindByIdAsync(userId).ConfigureAwait(false);
if (user == null)
throw new InvalidOperationException("Invalid user Id");
var userRoles = await userRoleStore.GetRolesAsync(user).ConfigureAwait(false);
// Add user to each role using UserRoleStore
foreach (var role in roles.Where(role => !userRoles.Contains(role)))
await userRoleStore.AddToRoleAsync(user, role).ConfigureAwait(false);
// Call update once when all roles are added
return await UpdateAsync(user).ConfigureAwait(false);
}
public virtual async Task<IdentityResult> RemoveUserFromRolesAsync(string userId, IList<string> roles)
{
var userRoleStore = (IUserRoleStore<ApplicationUser, string>) Store;
var user = await FindByIdAsync(userId).ConfigureAwait(false);
if (user == null)
throw new InvalidOperationException("Invalid user Id");
var userRoles = await userRoleStore.GetRolesAsync(user).ConfigureAwait(false);
// Remove user to each role using UserRoleStore
foreach (var role in roles.Where(userRoles.Contains))
await userRoleStore.RemoveFromRoleAsync(user, role).ConfigureAwait(false);
// Call update once when all roles are removed
return await UpdateAsync(user).ConfigureAwait(false);
}
}
If you want to read all the users in one short and store in the memory than you use the below style. And I strongly recommend you to read user only at the time of login for the you need to add your logic in "UserStoreService" class.
public class ApplicationDbContext : IDisposable
{
private ApplicationDbContext(IList<ApplicationUser> users)
{
Users = users;
}
public IList<ApplicationUser> Users { get; set; }
public void Dispose()
{
}
public static ApplicationDbContext Create()
{
//You can use any database and hook it here
var users = new List<ApplicationUser>
{
new ApplicationUser
{
UserName = "a#a.com",
Email = "a#a.com",
Password = "test",
Roles = new List<string> {"Admin", "Admin2"}
},
new ApplicationUser
{
UserName = "a#a2.com",
Email = "a#a2.com",
Password = "test2",
Roles = new List<string> {"Admin"}
}
};
return new ApplicationDbContext(users);
}
}
public class ApplicationOAuthProvider : OAuthAuthorizationServerProvider
{
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();
var user = await userManager.FindAsync(context.UserName.ToLower(), context.Password);
if (user == null)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
try
{
var oAuthIdentity = await userManager.CreateIdentityAsync(user, context.Options.AuthenticationType);
var cookiesIdentity = await userManager.CreateIdentityAsync(user,
CookieAuthenticationDefaults.AuthenticationType);
var props = new AuthenticationProperties(new Dictionary<string, string>
{
{
"client_id", context.ClientId == null ? string.Empty : context.ClientId
},
{
"userName", context.UserName
}
});
var ticket = new AuthenticationTicket(oAuthIdentity, props);
context.Validated(ticket);
context.Request.Context.Authentication.SignIn(cookiesIdentity);
}
catch (Exception ex)
{
Trace.TraceError("FUSION Error ::: " + ex.Message + ex.InnerException);
Trace.TraceError(ex.Message);
}
}
public override Task TokenEndpoint(OAuthTokenEndpointContext context)
{
foreach (var property in context.Properties.Dictionary)
if (property.Value != null)
context.AdditionalResponseParameters.Add(property.Key, property.Value);
return Task.FromResult<object>(null);
}
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
// Resource owner password credentials does not provide a client ID.
if (context.ClientId == null)
context.Validated();
return Task.FromResult<object>(null);
}
}
public class AppPasswordHasher : IPasswordHasher
{
public string HashPassword(string password)
{
return password;
}
public PasswordVerificationResult VerifyHashedPassword
(string hashedPassword, string providedPassword)
{
if (hashedPassword == HashPassword(providedPassword))
return PasswordVerificationResult.Success;
return PasswordVerificationResult.Failed;
}
}
Method like "FindByNameAsync" ; where you need to read user from db on demand/login
public class UserStoreService<TUser> : IUserStore<TUser>,
IUserPasswordStore<TUser>,
IUserRoleStore<TUser>
where TUser : ApplicationUser
{
private readonly IList<TUser> _users;
public UserStoreService(IList<TUser> users)
{
_users = users;
}
public virtual Task SetPasswordHashAsync(TUser user, string passwordHash)
{
user.Password = passwordHash;
return Task.FromResult(0);
}
public virtual Task<string> GetPasswordHashAsync(TUser user)
{
return Task.FromResult(user.Password);
}
public virtual Task<bool> HasPasswordAsync(TUser user)
{
return Task.FromResult(user.Password != null);
}
public virtual Task AddToRoleAsync(TUser user, string roleName)
{
user.AddRole(roleName);
return Task.FromResult(0);
}
public virtual Task RemoveFromRoleAsync(TUser user, string roleName)
{
user.RemoveRole(roleName);
return Task.FromResult(0);
}
public virtual Task<IList<string>> GetRolesAsync(TUser user)
{
return Task.FromResult((IList<string>) user.Roles);
}
public virtual Task<bool> IsInRoleAsync(TUser user, string roleName)
{
return Task.FromResult(user.Roles.Contains(roleName));
}
public virtual void Dispose()
{
}
public virtual Task CreateAsync(TUser user)
{
user.CreatedTime = DateTime.Now;
user.UpdatedTime = DateTime.Now;
_users.Add(user);
return Task.FromResult(true);
}
public virtual Task UpdateAsync(TUser user)
{
// todo should add an optimistic concurrency check
user.UpdatedTime = DateTime.Now;
_users.Remove(user);
_users.Add(user);
return Task.FromResult(true);
}
public virtual Task DeleteAsync(TUser user)
{
return Task.FromResult(_users.Remove(user));
}
public virtual Task<TUser> FindByIdAsync(string userId)
{
return Task.FromResult(_users.FirstOrDefault(u => u.Id == userId));
}
public virtual Task<TUser> FindByNameAsync(string userName)
{
// todo exception on duplicates? or better to enforce unique index to ensure this
return Task.FromResult(_users.FirstOrDefault(u => u.Email == userName));
}
}
[Authorize(Roles = "Admin")]
public class RolesController : ApiController
{
public IEnumerable<string> Get()
{
return new[] {"value3", "value4"};
}
}
Source Code (github)
It is bound to Entity Framework and SQL Server by default, but you can easily plug in other data stores such as SharePoint, Windows Azure Storage Table Service, NoSQL databases, etc., and you get to retain control of the database schema.
Further Reading
Introduction to ASP.NET Identity
There is a "JRE Class White List" for the Google App Engine.
What I would really like is a "Black List" -- in other words, Java APIs that will not work on GAE. Does such a list exist? Have any developers run into problems with Java APIs on GAE?
It seems that they've taken more of a white-list approach: http://code.google.com/appengine/docs/java/jrewhitelist.html.
There is also more detail about the sandbox (what files it can access and so on) here: http://code.google.com/appengine/docs/java/runtime.html#The_Sandbox
The restrictions seem to be pretty intuitive (like restricted filesystem access, no JNI, etc).
I got a card advertising this service at Google I/O:
LTech AppEngine Compatibility Analyzer
Sounds like it might be of use to you. I have not tried it, if you do try it, please come back and comment. Thanks!
i use Servlet in my GAE project, however it is not in the whitelist even when it will work without any problem. In fact, Google mention how to use Servlet but it is not in the whitelist
import javax.servlet.http.*;
Mentioned here:
http://code.google.com/appengine/docs/java/runtime.html
but not included here:
http://code.google.com/appengine/docs/java/jrewhitelist.html
I love GAE (because the free quota) but the documentation is a mess.
I use IntelliJ and it mark as an error when the import not in the whitelist. However, it is possible to disable it.
I was looking for something when i came across this query and so thought to share the details on the black & white list of GAE(Google App Engine) so anyone getting such issue could address it properly. Details :-
appengine-agentruntime.jar has two instance variables as :-
private static Agent agent
private static Set<String> blackList
We getting blackList from agent & agent = AppEngineDevAgent.getAgent(). So if we check b) appengine-agent.jar we can find agent is Class<?> implClass = agentImplLoader.loadClass("com.google.appengine.tools.development.agent.impl.AgentImpl");
And then going to AgentImpl class i.e. c) appengine-agentimpl.jar we can
see blacklist variable getting populated at class load with the static initialization & it refers Whitelist for filtering the allowed classes.
static {
initBlackList();
}
public static Set<String> getBlackList() {
return blackList;
}
private static boolean isBlackListed(String className) {
Set<String> whiteList = WhiteList.getWhiteList();
return (!whiteList.contains(className))
&& (!className.startsWith("com.sun.xml.internal.bind."));
}
Finally can check d) appengine-tools-sdk-1.8.3.jar for list of all WhiteList classes.
Conclusion: As a hack in order to use any JRE class which do not belong to this WhiteList one need to play around either with the WhiteList or with the BlackList. A possible hack would be if you unjar the appengine-agentruntime.jar library & comment the content of reject method as
public static void reject(String className) {
/*throw new NoClassDefFoundError(className + " is a restricted class. Please see the Google " + " App Engine developer's guide for more details.");*/
}
And then again jar it and use in your project.Hope it helps.
-----------------------------------------------------------------------------
a) appengine-agentruntime.jar :- It holds the actual Runtime class which throws exception (from reject method) for classes which do not belong to above white list.
package com.google.appengine.tools.development.agent.runtime;
import com.google.appengine.tools.development.agent.AppEngineDevAgent;
import com.google.appengine.tools.development.agent.impl.Agent;
import com.google.apphosting.utils.clearcast.ClearCast;
//REMOVED OTHER IMPORTS TO KEEP IT SHORT
public class Runtime {
private static Agent agent = (Agent) ClearCast.cast(
AppEngineDevAgent.getAgent(), Agent.class);
private static Set<String> blackList = agent.getBlackList();
public static ClassLoader checkParentClassLoader(ClassLoader loader) {
ClassLoader systemLoader = ClassLoader.getSystemClassLoader();
return (loader != null) && (loader != systemLoader) ? loader
: Runtime.class.getClassLoader();
}
public static void recordClassLoader(ClassLoader loader) {
agent.recordAppClassLoader(loader);
}
public static void reject(String className) {
throw new NoClassDefFoundError(className
+ " is a restricted class. Please see the Google "
+ " App Engine developer's guide for more details.");
}
private static boolean isBlackListed(Class klass) {
String className = klass.getName().replace('.', '/');
return blackList.contains(className);
}
// REMOVED OTHER METHODS TO KEEP IT SHORT
}
b) appengine-agent.jar :-
package com.google.appengine.tools.development.agent;
import com.google.apphosting.utils.clearcast.ClearCast;
//REMOVED OTHER IMPORTS TO KEEP IT SHORT
public class AppEngineDevAgent {
private static final String AGENT_IMPL = "com.google.appengine.tools.development.agent.impl.AgentImpl";
private static final String AGENT_IMPL_JAR = "appengine-agentimpl.jar";
private static final Logger logger = Logger.getLogger(AppEngineDevAgent.class.getName());
private static Object impl;
public static void premain(String agentArgs, Instrumentation inst) {
URL agentImplLib = findAgentImplLib();
URLClassLoader agentImplLoader = new URLClassLoader(
new URL[] { agentImplLib }) {
protected PermissionCollection getPermissions(CodeSource codesource) {
PermissionCollection perms = super.getPermissions(codesource);
perms.add(new AllPermission());
return perms;
}
};
try {
Class<?> implClass = agentImplLoader
.loadClass("com.google.appengine.tools.development.agent.impl.AgentImpl");
impl = ((AgentImplStruct) ClearCast.staticCast(implClass,
AgentImplStruct.class)).getInstance();
AgentImplStruct agentImplStruct = (AgentImplStruct) ClearCast.cast(
impl, AgentImplStruct.class);
agentImplStruct.run(inst);
} catch (Exception e) {
logger.log(
Level.SEVERE,
"Unable to load the App Engine dev agent. Security restrictions will not be completely emulated.",
e);
}
}
public static Object getAgent() {
return impl;
}
//REMOVED OTHER METHODS TO KEEP IT SHORT
}
c) appengine-agentimpl.jar :-
package com.google.appengine.tools.development.agent.impl;
import com.google.apphosting.runtime.security.WhiteList;
//REMOVED OTHER IMPORTS TO KEEP IT SHORT
public class BlackList {
private static final Logger logger = Logger.getLogger(BlackList.class.getName());
private static Set<String> blackList = new HashSet();
static {
initBlackList();
}
public static Set<String> getBlackList() {
return blackList;
}
private static boolean isBlackListed(String className) {
Set<String> whiteList = WhiteList.getWhiteList();
return (!whiteList.contains(className))
&& (!className.startsWith("com.sun.xml.internal.bind."));
}
private static void initBlackList() {
Set<File> jreJars = getCurrentJreJars();
for (File f : jreJars) {
JarFile jarFile = null;
try {
jarFile = new JarFile(f);
} catch (IOException e) {
logger.log(
Level.SEVERE,
"Unable to read a jre library while constructing the blacklist. Security restrictions may not be entirely emulated. "
+ f.getAbsolutePath());
}
continue;
Enumeration<JarEntry> entries = jarFile.entries();
while (entries.hasMoreElements()) {
JarEntry entry = (JarEntry) entries.nextElement();
String entryName = entry.getName();
if (entryName.endsWith(".class")) {
String className = entryName.replace('/', '.').substring(0,
entryName.length() - ".class".length());
if (isBlackListed(className)) {
blackList.add(className.replace('.', '/'));
}
}
}
}
blackList = Collections.unmodifiableSet(blackList);
}
private static Set<File> getCurrentJreJars() {
return getJreJars(System.getProperty("java.home"));
}
//REMOVED OTHER METHODS TO KEEP IT SHORT
}
d) appengine-tools-sdk-1.8.3.jar :- It has a class called WhiteList which includes all allowed JRE classes.
package com.google.apphosting.runtime.security;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
public class WhiteList {
private static Set<String> whiteList = new HashSet(
Arrays.asList(new String[] {
"java.beans.Transient",
"java.lang.BootstrapMethodError",
"java.lang.Character$UnicodeScript",
"java.lang.ClassValue",
"java.lang.SafeVarargs",
//Removed other classes to keep this article short
"java.net.URLClassLoader",
"java.security.SecureClassLoader",
"sun.net.spi.nameservice.NameService" }));
public static Set<String> getWhiteList() {
return whiteList;
}
}