Weblogic, Kerberos, Single Sign On and You

We recently needed to implement single sign on in Weblogic for a client and finally managed to get it working, but had more difficulty than we probably ought to have. While some of our problems stemmed from our lack of experience with Weblogic, we also ran into problems with the documentation we were pointed to being vague, incomplete, and/or misleading. My goal with this post is to hopefully help others avoid some of the same misunderstandings and pitfalls we experienced.

First, I’ll point you to three pertinent links which helped. Understand, though, that there were a number of other things we ran across through various searches that led to us working these issues out. The first two are official Oracle documents and the third was a scribd article that helped us put some pieces together.

How To Configure Browser-based SSO with Kerberos/SPNEGO and Oracle WebLogic Server

Configuring Single Sign-On with Microsoft Clients

Enable Kerberos With Workspace 11.1.1.3

As I said, the first two links are to Oracle documentation. I believe the second link is a bit more useful, to be honest, though the first does have a little sample servlet code you can slap into place for testing; but keep in mind that the instructions to make it work are incomplete. The useful bits on both have more to do with configuring Kerberos (e.g. account and SPN generation, keytab generation and installation) but leave a little out that would be very useful. The third article is a scribd article that describes exactly what the title indicates, but the setup for Kerberos was helpful in terms of getting the keytab right.

The first problem we had was with the account and keytab generation. We were using the first Oracle link which was a little more spartan with its explanation of how to set up the accounts in Active Directory and set up the keytab file. Sure, it has images and explanations but the selection of account and machine names is ridiculously confusing. The second Oracle link has a better explanation of the account setup and keytab generation but still includes some information that is either unnecessary or misleading.

  1. In the Active Directory create a user account which will be doing ‘Kerberos things’. e.g. svc_someapp_krb
    1. By all means, ignore the suggestion of using the name of the client machine for the account name. Adhere to whatever naming convention makes the most sense. This one suggestion led to a lot of confusion while trying to decide how to configure the ‘ktpass’ command.
    2. ‘ktpass’ will create an SPN for you, but you can also do it yourself. The instructions suggest creating the SPN separately. It matters little.
    3. Keep in mind what encryption type to select in AD for the user. It must be consistent. We used AES 128. If you go higher than this remember you’ll need to download Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files here and install them properly.
    4. Make note of the fact that changing the encryption type AFTER setting your password can corrupt the encrypted password. Reset your password (even if to the current value) if you change encryption.
    5. Sometimes Java implementations for keytabs have problems with special characters. What is a special character? We wound up using strictly alphanumerics to avoid the problem. We couldn’t find a firm definition of what was allowed. You could try underscores and hyphens. Much more than that and you begin risking running into this issue.
  2. For our ‘ktpass’ our principal was of the form ‘HTTP/ourserver.some.domain.com@SOME.DOMAIN.COM’. Notice the caps? You’ll need that. Notice the repetition of the domain? You’ll need that to. So our ‘ktpass’ was like:
    1. ktpass /princ HTTP/weblogicserver.domain.com@DOMAIN.COM /pass domainuserpassword  /mapuser domainuser@domain.com /ptype KRB5_NT_PRINCIPAL /out weblogic.keytab /crypto AES128-SHA1 /kvno 0
  3. If you are not using node manager, but are running your Weblogic server as a Windows service, you presumably already set up your install.cmd file. You’ll need to add the specified options as JAVA_OPTIONS here. Remember, the Server->Server Start arguments are only useful when the Node Manager is handling things on a remote server. For local setup you’ll need to use different means. We ended up using full paths for our various files.
  4. If you don’t have access to the C:\Windows\ folder, you’ll need to create your krb5.ini file and place it somewhere else, then add a startup option to point to it as the default location can’t be used. You can do this via the following additional JAVA_OPTIONS parameter:
    1. -Djava.security.krb5.conf=C:\OurApp\krbstuff\krb5.ini

Keeping these points in mind while setting up things for Kerberos will help you avoid some of the issues we ran into. At this point we were able to add the NegotiateIdentityProvider to our security realm. The Kerberos debug settings allowed us to see that the ticket setup was working, plus kinit confirmed things too. But the test servlet still wasn’t working.

So what went wrong next? For starters, our inexperience with Weblogic led to a lack of understanding of how the authentication system works. Let’s take a look at that sample servlet in the first link. The weblogic.xml snippet (well, it’s complete but tiny for sample purposes) looks like this:

<!DOCTYPE weblogic-web-app PUBLIC "-//BEA Systems, Inc.//DTD Web Application 8.1//EN" "http://www.bea.com/servers/wls810/dtd/weblogic810-web-jar.dtd">
<weblogic-web-app>
  <security-role-assignment>
    <role-name>negotiateAdminRole</role-name>
    <principal-name>negotiateAdmin</principal-name>
    <principal-name>Administrators</principal-name>
  </security-role-assignment>
</weblogic-web-app>

If you understand this part, please skip it. This is just saying that “if something in the Weblogic security layer declares that the current visitor is a ‘negotiateAdmin’ thing or an ‘Administrators’ thing, then for this servlet we will treat it is a ‘negotiateAdminRole’ thing”. I’m dumbing it down a bit, but given our inexperience with Weblogic when we were setting things up, we didn’t yet know how Weblogic would determine that the current user was a ‘negotiateAdmin’ or an ‘Administrators’ member. We had the notion of an LDAP or AD (via LDAP) lookup but nothing had yet crystallized.

Now for a snippet from the sample web.xml:

    <security-constraint>
        <web-resource-collection>
            <web-resource-name>BasicAuthSimpleTestServlet</web-resource-name>
            <url-pattern>/*</url-pattern>
            <url-pattern>/</url-pattern>
            <http-method>POST</http-method>
            <http-method>GET</http-method>
        </web-resource-collection>
        <auth-constraint>
            <role-name>negotiateAdminRole</role-name>
        </auth-constraint>
        <user-data-constraint>
            <description>no description</description>
            <transport-guarantee>NONE</transport-guarantee>
        </user-data-constraint>
    </security-constraint>
    <security-role>
        <role-name>negotiateAdminRole</role-name>
    </security-role>
    <login-config>
        <auth-method>BASIC</auth-method>
        <realm-name>default</realm-name>
    </login-config>

Starting from the bottom and going up, check out the auth-method. BASIC is going to trigger a login prompt. We want single sign on and this page is meant to be an example of such using Kerberos. You want CLIENT-CERT here, not BASIC. CLIENT-CERT says “hey, browser, you are GOING to give me a certificate of some kind, including possibly a Kerberos ticket, or you will be denied”. Quite simple. I suppose the sample was intended to give a visual cue but typically a browser would be configured to do that anyway if an ‘Authorization: Negotiate’ header came back from the server so again I’m not sure why this change was left in. Anyhow…

Moving up, we declare the existence of a security-role called ‘negotiateAdminRole’. This makes it available for reference elsewhere, in this case in the auth-constraints tag agove it. If you take a look in the security-constraint you get the gist of what is going on; the servlet is being set up with its available access methods and is constrained to only those users with the negotiateAdminRole assigned. Now, recall the web.xml where we indicated who would be granted the negotiateAdminRole? That made some sense to us, but what was missing was the following:

The mapping of principal-name to role-name in web.xml is based on mapping whatever identity is retrieved from authentication providers to retrieved roles from various role providers via a simple string match.

Let’s jump ahead a bit. Because of the NegotiateIdentityProvider in our security realm, Weblogic knew what our account name was due to the Kerberos ticket authentication taking place. So user ‘jsmith’ comes along and now the current user is identified as user ‘jsmith’. But ‘jsmith’ doesn’t belong to any roles and gets no principals assigned. Weblogic will take the user account and check with other providers to see if they know who ‘jsmith’ is and see if it has any group/role memberships.

It turns out that to make the sample servlet work, you can simply add a user to the built in Weblogic Users/Groups with the same account name as an account from the Kerberos domain, in this case ‘jsmith’. Now add ‘jsmith’ to a Weblogic group as used in the sample servlet, e.g. ‘Administrators’. That will immediately make the sample servlet start working assuming you are ‘jsmith’. Moreover, it means that ‘jsmith’ will become an Administrator when hitting the Weblogic console so you now have single sign on working for your console.

But now the next part comes in, linking to your Active Directory implementation for LDAP lookups. Just as you added the NegotiateIdentityProvider, you can also add an ActiveDirectoryAuthentication provider as well as LDAPAuthentication. We read a number of articles suggesting that even if you are targeting Active Directory, you may still want to use LDAPAuthenticator to work around some problems. We in fact tried LDAP first but ended up getting ActiveDirectoryAuthenticator to work for us. The trick boils down to what you use for the Principal name and the various filters. You need an account you can log in with, preferably a service account of some kind. Some suggested you could log in with the Principal set to just the account name. We ended up having to use the full distinguished name. As for your filters, you’ll have to consult with your AD admin, or be capable of examining your LDAP structure to draw your own conclusions. Keep in mind that if you don’t fill in the fields for the ‘All Users’ and ‘All Groups’ filters, then the Users/Groups tabs will not be auto populated with the users/groups from AD.

Once you have this hooked up, you can start referencing groups that your Active Directory users are members of for security purposes. None of the actual steps in the process are necessarily difficult. What made this difficult for us was more to do with lack of complete and cogent documentation. This post isn’t sufficient on its own to get you where you need to be, but hopefully will help clarify some of what you will read on other pages. Good luck!

Have You Seen This SQL Server Bug?

I may have found a bug regarding how SQL Server calls CLR functions when using columns from tables joined using non-indexed columns. I’m including a SQL script, with comments on what to do with it, and hopefully anyone reading this will try it for themselves and tell me if what I’m seeing happens for them and whether I’m misinterpreting things. I’m willing to admit if I’m misunderstanding how things are supposed to work but so far everyone that I have spoken with about this is as perplexed as I am. And yet none of us can imagine this has never been noticed before and also is working as intended.

For starters, I discovered this in 64 bit SQL Server 2008 R2. One of my coworkers, our DBA, also reproduced this on 64 bit SQL Server 2012. I posted the issue at stackoverflow but no one even commented on it. My summary of the problem as I posted there is as follows:

SQL Server (at least on 2008 R2, 64 bit), when processing a query that includes a CLR function call in the SELECT clause and when using at least one column from a table that is included via JOIN on non-indexed columns, appears to invoke that CLR function for every row not constrained by either an indexed column in the JOIN or by an explicit WHERE clause item.

Now for some more details. This all has to do with the Microsoft spatial objects CLR functionality. Suffice to say I have a table full of extents, latitude/longitude pairs marking the opposing corners of a region. Additionally I have an SRID (spatial reference ID) to indicate what coordinate system was used. Some use meters offset, some use degrees. Also, the data I have is dirty. It has NULL values, invalid SRID values, coordinates that don’t match the SRID (i.e. meter offsets recorded for a supposedly degree based SRID). Finally, not only did I need to filter out potential offending values I also had additional filters being joined in from other tables. So my original SQL was actually doing a pre-filter pass to generate a valid set of ID values followed by the actual SELECT which joined this list of pre-filtered keys in a table variable with the original data table.

Part of the SELECT was to make use of the geography::STGeomFromText() call to return a geography object as a column in the result set. No sweat, my tests showed only valid rows were returning. But when I added that call, I began getting exceptions from the underlying CLR complaining about invalid geographies, invalid SRID values, insufficient numbers of points in the ring, etc. After a considerable amount of debugging and testing, I came up with the script listed below.

The first chunk of the script is commented out and includes two sections. One section creates a FUNCTION that takes a VARCHAR and an INT and just passes them directly to geography::STGeomFromText(). It is a simple wrapper. More on that in a bit.

The second section in the commented out area sets up the data table. The data table happens to include 7 test records but could probably work with fewer. I hit the exact results I was hunting down after I had added the 7th record and as much out of superstitious need to cling to my voodoo doll as anything else, I did not alter the data set. In any case, the CREATE TABLE is most notable for NOT indexing the ID column. The column is also not an IDENTITY but honestly making it an IDENTITY column did not impact the outcome and just added needless complexity.

The uncommented code that follows declares a table variable to hold valid keys, populates the table variable, and then performs a SELECT, returning not only all columns from the original data table but also additional columns which directly invoke the CLR function as well as call the UDF created up top. There is also a commented out WHERE clause.

I’ll summarize what I saw before I dump the script.

  • If I do not include the CLR call as a column in the SELECT, the SELECT works and I only see records which have valid values that would work in a call to the CLR function.
  • If I call the UDF, a wrapper around the CLR, I again only see the expected records and I get valid geography objects returned for each.
  • If I call the CLR, I receive an exception about an invalid SRID (being NULL). Depending on the results of the full table scan, you may also receive errors about invalid geography point values. Regardless, the query fails.
  • If I uncomment the WHERE clause, I can now include the CLR call as a column with no errors.
  • If the WHERE clause is commented out but the original data table is instead recreated such that the id field is a PRIMARY KEY field (or really, indexed in any way but the obvious approach would be to make it a PRIMARY KEY), the CLR function may also be included in the SELECT without issue.

What I believe is happening is that the main query is joining two tables, the data table and the table with a simple list of keys. It joins these tables on the ID column. When there is an INDEX on the data table’s ID column, SQL Server first joins on the results of the INDEX lookup, then fetches only the rows corresponding to the joined keys. Since the key table only has keys to rows with valid data, no invalid data is ever even seen. But when there is no INDEX, a table scan must be performed on the data table. While it is true that the result set will only end up including rows with valid data since that’s the only keys in the key table, it seems as though the CLR function is still being called for each row in the scan, regardless of whether it will end up in the final result set.

The interesting thing about the UDF is that it seems to mask whatever behavior is going on with the CLR function call. That is, the UDF does not get called for every row of a table scan, only for the rows which would be returned in the result set.

Aside from simple stating the obvious, that the lack of an INDEX forces a full table scan and the database is giving the CLR function a shot on each row before it is filtered out by the JOIN restriction (though, oddly enough, after any WHERE clause), I can’t see why this would happen. Wrapping it in a UDF seems to use a slightly different logic path. Perhaps it is a bug in the CLR assembly from Microsoft (this is Microsoft’s geography implementation) but I don’t have any other assemblies to test and our environment doesn’t allow us to install anything else.

Anyhow, here’s the script to reproduce the problem.

/*
 * This part of the script makes a UDF available to wrap the CLR call
 *
 * Only run this once
--
--
--
-- BEGIN RUN ONCE
--
--
--
IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[fLPPTestMakeGeography]') AND type in (N'FN', N'IF', N'TF', N'FS', N'FT'))
DROP FUNCTION [dbo].[fMakeGeography]
GO
CREATE FUNCTION [dbo].[fLPPTestMakeGeography]
(
    @p1 varchar(max)
    , @p2 int
)
RETURNS geography
AS
BEGIN
    DECLARE @Result geography
    -- just a wrapper around the geography::STGeomFromText call
    SELECT @Result = geography::STGeomFromText(@p1,@p2)
    RETURN @Result
END
GO
--
--
--
-- END RUN ONCE
--
--
--
*/
/*
 * Likewise this sets up the test table.
 * Note that this all goes away when a PRIMARY KEY is placed on the id column
 * Commentary: by making id a PRIMARY KEY, it causes the join in the main query
 * to do an index lookup rather than doing a full scan of the data table. Without
 * that PRIMARY KEY setting, even though the table variable is joined to the data
 * table in such a way as to eliminate any potential invalid rows, every row of
 * the data table gets examined. The fact that the CLR call throws an exception
 * in this case but the UDF call does not suggests the following:
 * For each row of a table that is not filtered in the JOIN clause via an index
 * and not filtered in the WHERE clause directly, any CLR call in the SELECT
 * clause will be called if it involves any columns from that row.
 * Note that this behavior differs from that of UDFs, where the UDF call only
 * takes place once *all* filtering is done, including non-INDEXed portions of
 * any JOIN statements.
--
--
--
-- BEGIN RUN ONCE
--
--
--
drop table dbo.LPPTestEnvelope
create table dbo.LPPTestEnvelope
(
id int not null
, geog int
, minx decimal(38,16)
, maxx decimal(38,16)
, miny decimal(38,16)
, maxy decimal(38,16)
)
insert into dbo.LPPTestEnvelope (id, minx, maxx, miny, maxy, geog)
select 1,-80,-70,20,30,4326 union all
select 2,-80,-80,20,20,NULL union all
select 3,-80,-75,20,25,4326 union all
select 4,NULL,NULL,NULL,NULL,4326 union all
select 5,-85,-70,25,40,4326 union all
select 6,NULL,NULL,NULL,NULL,4326 union all
select 7,-100000,-90000,3000000,3100000,4326
--
--
--
-- END RUN ONCE
--
--
--
*/
-- select * from dbo.LPPTestEnvelope
set nocount on;
-- in a more complex setting this table variable
-- would be used to do some complex pre-queries and hold the
-- key values which would be joined to the original data
-- table to get the actual rows desired
--
-- for this example it's just dumbed down but the concept holds
declare @envtbl table
(
    eid int not null primary key
)
INSERT INTO @envtbl (eid)
SELECT
    id
FROM
    dbo.LPPTestEnvelope env
WHERE
    -- exclude invalid SRID values
    env.geog=4326
    -- exclude invalid lat/long values (e.g. UTM values with improper SRID assignments)
    AND env.minx > -180 AND env.minx < 180 AND env.maxx > -180 AND env.maxx < 180
    AND env.miny > -90 AND env.miny < 90 AND env.maxy > -90 AND env.maxy < 90
    -- avoid precision bug
    AND ABS(env.maxx-env.minx)>1E-7 AND ABS(env.maxy-env.miny)>1E-7
    -- avoid crossing hemispheres
    AND SIGN(env.maxx)=SIGN(env.minx) AND SIGN(env.maxy)=SIGN(env.miny)
SELECT
    -- display the raw data from the table
    env.*
    -- directly invoke the CLR to create a geography object
    -- NOTE: COMMENT THIS COLUMN OUT TO MAKE THE QUERY WORK
    ,geography::STGeomFromText('POLYGON(('
            + CAST(env.minx AS VARCHAR) + ' ' + CAST (env.maxy AS VARCHAR) + ', '
            + CAST(env.minx AS VARCHAR) + ' ' + CAST (env.miny AS VARCHAR) + ', '
            + CAST(env.maxx AS VARCHAR) + ' ' + CAST (env.miny AS VARCHAR) + ', '
            + CAST(env.maxx AS VARCHAR) + ' ' + CAST (env.maxy AS VARCHAR) + ', '
            + CAST(env.minx AS VARCHAR) + ' ' + CAST (env.maxy AS VARCHAR)
            + '))', env.geog)
    ,[dbo].[fLPPTestMakeGeography]('POLYGON(('
            + CAST(env.minx AS VARCHAR) + ' ' + CAST (env.maxy AS VARCHAR) + ', '
            + CAST(env.minx AS VARCHAR) + ' ' + CAST (env.miny AS VARCHAR) + ', '
            + CAST(env.maxx AS VARCHAR) + ' ' + CAST (env.miny AS VARCHAR) + ', '
            + CAST(env.maxx AS VARCHAR) + ' ' + CAST (env.maxy AS VARCHAR) + ', '
            + CAST(env.minx AS VARCHAR) + ' ' + CAST (env.maxy AS VARCHAR)
            + '))',env.geog)
    ,'SELECT geography::STPolyFromText(''POLYGON(('
            + CAST(env.minx AS VARCHAR) + ' ' + CAST (env.maxy AS VARCHAR) + ', '
            + CAST(env.minx AS VARCHAR) + ' ' + CAST (env.miny AS VARCHAR) + ', '
            + CAST(env.maxx AS VARCHAR) + ' ' + CAST (env.miny AS VARCHAR) + ', '
            + CAST(env.maxx AS VARCHAR) + ' ' + CAST (env.maxy AS VARCHAR) + ', '
            + CAST(env.minx AS VARCHAR) + ' ' + CAST (env.maxy AS VARCHAR)
            + '))'',' + CAST(env.geog AS VARCHAR) + ')'
FROM
    -- join our data table to our table variable to fetch only the desired rows
    dbo.LPPTestEnvelope env
    INNER JOIN @envtbl et on et.eid=env.id
-- NOTE: UNCOMMENT THE WHERE CLAUSE TO MAKE THE QUERY WORK REGARDLESS OF INDEX
--WHERE
--  -- exclude invalid SRID values
--  env.geog=4326
--  -- exclude invalid lat/long values (e.g. UTM values with improper SRID assignments)
--  AND env.minx > -180 AND env.minx < 180 AND env.maxx > -180 AND env.maxx < 180
--  AND env.miny > -90 AND env.miny < 90 AND env.maxy > -90 AND env.maxy < 90
--  -- avoid precision bug
--  AND ABS(env.maxx-env.minx)>1E-7 AND ABS(env.maxy-env.miny)>1E-7
--  -- avoid crossing hemispheres
--  AND SIGN(env.maxx)=SIGN(env.minx) AND SIGN(env.maxy)=SIGN(env.miny)

Short Term Pain for Long Term Gain

A recent story that’s shown up a number of places concerns a Houston couple giving away around $4 billion for causes they deem worthwhile and, more importantly, that appear to be provably effective. The idea being that they want to make sure their dollars go toward things which have a higher likelihood of permanent change and for which metrics are in place or can be put in place to measure that effectiveness. Additionally they want proposals to have already thought things through, showing the path from getting to where the problem is now to where the solution may be. That said, supposedly there is some sort of backlash over this because they are spending their money on things which do not necessarily help people in need right now.

Let’s overlook the absurdity of telling them how they can spend their money for a moment. After all, if that were an issue we should be storming the gates of every rich person out there who lives the playboy/playgirl lifestyle and does precious little for their fellow human and we don’t see that. As far as I’m concerned the fact that this couple is spending any of their money on social reform is a good thing and to be lauded.

I dislike the argument that they should focus their efforts on short term solutions to long term problems, which is one aspect of the argument being made against their activities. To begin with there are already a lot of entities providing help to the needy in many aspects. Could more money help some of them? Absolutely. But wouldn’t it be better if, instead of pouring money into addressing the symptom you put money into finding a cure? If instead of simply buying more aspirin, you could stop the fever and watch it die out?

There are some, however, who don’t so much disagree with the idea of finding a permanent solution as they do with the hubris associated for daring to believe that some solutions can be found or even paid for. And yet, why not at least try? If nothing else some of the money spent on trying to find and enact a solution to a social ill could turn up information that leads to an actual approach that works or just increased awareness of the problem. Again, all of this is a positive outcome. At worst, the money simply disappears into a black hole with zero results. That would be unfortunate on many levels but not a catastrophe for anyone besides the funding couple.

Personally I am excited to see this approach taken. While I firmly believe that too much measurement can bog down a process or a system, I also believe the opposite end of the spectrum is adverse as well; too little measurement results in massive inefficiency. In this case the measurement isn’t even focused on whether an organization is spending their money on those in need, getting by on as little as possible, etc. It is solely focused on getting the most long term bang for the buck, seeing to it that the focus is on eliminating or at least scaling down the problem as much as possible rather than just treating the symptoms. If this works, and any effects would necessarily be felt only in the long term, it could become a blueprint for how to best go about addressing issues that have plagued mankind for far too long. I can’t wait to see how it turns out!

New Stalemate Update Is Available

Like it says on the tin, the new update for Stalemate is available, get it while it’s hot!

Also, it’s free.. well the update was free, but now I’ve made the app free too. And Centripetal as well! Y’all have fun!

Stalemate Updates Coming Soon

I’ve recently been revisiting my little iOS chess like app, Stalemate. The latest update is currently in review and includes two major revisions. The first and most important is the addition of a tutorial mode. That is a feature that has been requested and honestly should have been there from the word go. When I put Centripetal together, the game play was simple enough that a simple help screen was sufficient with some explanation of the goal and obstacles. With Stalemate the scoring needed to be explained, the fact that pieces don’t move once placed was probably not what folks expected and though simple it is still more complex than Centripetal. So yeah, should have made a tutorial to begin with.

The second and less important but more visible change is with the look and feel. I moved away from my attempt at aged parchment with some sort of Renaissance gradient font (seriously, what was I thinking?) to a simple black and white glossy look more in line with the icon set I used for the pieces in the first place. Here’s the new splash page:

Splash screen for Stalemate iPhone app

Splash screen for Stalemate iPhone app

Okay, yes, I’ve picked another custom font. But this time there is no gradient at least. When I created the reflection, at first I just made it a normal reflection but then it occurred to me to reflect (see what I did there?) the oppositional nature of the game and therefore made the reflected portion a negative of the full portion. I may not have a future as a graphic design artist but I thought it was a nice touch.

Update – We’re now live with the new look. In addition, I’ve made Stalemate and Centripetal free. I think I would rather see them getting played than continue to see a trickle of income off of them. Have fun!

Centripetal Re-released!

Greetings and salutations and welcome to 2013! For my first new post of the year I am very happy to announce that I have re-released Centripetal onto the iTunes App Store under my own name. :) Click here to purchase Centripetal on the iTunes App Store.

As an aside, a fair bit has changed since I first released Centripetal. I had used cocos2d 0.99 at the time. XCode 3 was all the rage. iOS was at v4.x and OpenGL ES 1.0 was the standard.

Now cocos2d 2.0 is out, XCode is at 4.5, iOS is up to v6.x and OpenGL ES 2.0 is en vogue on our favorite mobile platform. As a result, it wasn’t enough to just recompile and resubmit. I ended up upgrading cocos2d, box2d, CocosDenshion and the other related libs. I also needed to tweak some of the code that I happened to have which made use of the GL_POINTS_SMOOTH parameter in the call to glEnable. By tweak I mean remove. You’ll see that on the help screens where the dot indicator showing which page you are on is now a square and not a circle. Yay progress?!

I do, however, want to also release this for OS X on the Mac App Store as well. Oddly enough I discovered it is enjoyable to play on the desktop due to running it in the iOS simulator.

Anyhow, if you already have your original copy, know that this is purely a re-release. If you haven’t tried it out before, you can check out my Centripetal page here (or click the link at the top of the page in the header) and watch the gameplay video.

Mitt-Math

Mitt Romney has been proclaiming his tax plan is designed to lower tax rates in a revenue neutral manner without reducing the share of taxes paid by the wealthiest Americans and without increasing the tax burdens on middle class and poorer Americans. His general theme includes several points:

  • Cut all tax rates by 20% in all brackets
  • Eliminate a number of deductions
  • Provide a boost to the economy to increase revenue through tax collection

Note this is not the same as his five point plan. I’m just talking about the general tax related portions. I don’t see how this works and I’m not the only one.

Imagine for a moment that the taxes collected by the IRS form a pie. A very small number of tax payers, the wealthiest 5% or so in America, provide 60% of that pie. Other tax brackets make up the remaining 40%. If you cut all tax rates by 20%, it would be akin to cutting off the outermost portion of that whole pie. The relative sizes of the slice remain the same but you have a smaller pie. He said he doesn’t want the rich to pay a smaller share of overall tax revenue, and this is in keeping with that. He says he wants middle class families to not have to pay so much money and this works there too. He wants to do this in a revenue neutral manner and now we have some problems. What next?

Thing is, the US government is pretty hungry and wants that pie. You’ve made it smaller. We’re not revenue neutral. So now we eliminate some deductions. This would add some pie back into the mix. But, and here’s a problem that Romney has yet to address, he has not told us what deductions he would suggest eliminating. That’s pretty important. At one point, he suggested possibly eliminating the mortgage interest deduction. While his campaign later backed off the idea, that would have been problematic as it would disproportionately affect the middle class he says he doesn’t want to hurt. How? Simple… the mortgage interest deduction represents, in relative terms, a larger write off for middle class households than upper class. Take that away and that family is now paying a higher amount of their own money in taxes than they were before. But it gets better.

I watched the debate last night and was surprised that Romney was pitching the idea of eliminating capital gains taxes as a boon to the middle class. The fact is that upper class earners would benefit far more than middle class earners if the capital gains tax were eliminated. So if this were pushed through, not only would middle class families not see much of a positive from this, now the taxes paid by upper class earners are going to go down, quite a bit. For the wealthiest earners, most of their income comes from capital gains, not from regular income. That is why their overall tax rate is so much lower than the rest of us. Eliminate taxes on capital gains and you eliminate taxes on the bulk of the money they earn. All of a sudden, not only is the pie much smaller but that 60% has shrunk a bit too.

The final piece of the puzzle is stimulating the economy. This is where Romney pitches his various plans to help create jobs and increase trade. With more money earned, more taxes will be paid. Great! Except capital gains are now off the table, so the business owners who are now making more money aren’t paying any more in taxes. So who is picking up the tab here? Oh, the middle and lower classes of income earners. The ones Romney says he doesn’t want to make pay more.

Now, whether you think it is fair that 5% of Americans are responsible for providing 60% of the tax revenue is an entirely different question. The point here is that Romney’s plan does not work as he says it does. And the biggest tell is that he refuses to provide any details. There’s a lot of hand waving but when it comes down to it, his plan is less of a plan than a wishlist. If he starts laying out details and those details make sense, I’ll be the first to agree that it is a workable solution. But until then all I’ve seen are smoke and mirrors.

 

Come on Apple… Amaze Me

So you might have heard about a new phone that’s out now. They call it the iPhone 5 and it is apparently the best phone Apple has ever created.

I don’t disagree. I like that they’ve gone back to a non-glass back. I like even more that it’s smaller, lighter, more powerful and let’s be honest — it has an improved, albeit still proprietary, plug. No more trying to plug it in the wrong way? Yes please. Still, these are evolutionary in nature and really only mildly so. It’s going to sell like hotcakes, I’m sure. Though why people would want to use hotcakes as a cellphone is outside the scope of this post. And if I weren’t already happy with my iPhone 4 (not even a 4S), I would upgrade. I’ll take a pass for now and wait awhile. Still, there are some things I would have liked to have seen.

As I said, I like the new non-glass backing. But perhaps they could go further? With the cash reserves Apple has on hand, they could buy Nokia and make an iPhone capable of surviving a gunshot. At point blank range. With a .50 cal. Seriously, have you seen those things? The Nokias, not the .50 cals. The point being anything Apple can do to ruggedize the iPhone (while still maintaining that famous iPhone design aesthetic) would be much appreciated.

Speaking of an iPhone surviving catastrophes, what about water? I’ve reviewed the Otterbox case for iPhone 4 but have since quit using it because the rubber shell was beginning to tear near the headphone flap and I decided I was going to try using my iPhone au naturale. So far I haven’t had any disasters and I’m doing alright with transporting my iPhone in my pocket but while I would dislike dropping it, I don’t think it would cause the same amount of panic as dipping it in liquid would. To that end, would it be possible to use some sort of liquid repelling construction or substance to at least provide a little peace of mind? It would be nice to know my iPhone could be used in the rain, or on a train, and on a bus, with a friend named Gus… ok, I’ll stop now.

Moving down the wishlist, I’m going to add something that I suspect could be just an iOS feature as opposed to a hardware update. Wi-fi only network mode. Currently iOS presents you the option to either disable all networking (Airplane Mode), only use cell networking (by turning off Wi-Fi) or to use Wi-Fi/cell networking (using Wi-Fi when available and falling back to cell otherwise). But what if I don’t want to use cell networking at all, and just want to make use of Wi-Fi? Apple, can you give me some of that, please? (Look, I just said please. That means you have to now, right? Tim?) Okay, those of you in the know are aware of my failure here. Head over to the Settings app, go to General->Cellular, turn off the cellular data option. Grats, you are now WiFi only. *sigh*

Right now, you might be wondering about the dock connector. There’s been a number of comments about Apple missed a chance to win some points by switching to the ubiquitous micro-USB connector rather than their new proprietary Lightning connector (seriously guys? Thunderbolt and Lightning? really?). If this article is to be believed, there are some solid reasons not to use micro-USB and in fact I think it would be reasonable to offer it as a new standard and to start switching phones to support it. That said, others have commented that while Apple might be justified in using their new connector, they should have offered adapters. That I also disagree with. If you’re buying a new iPhone 5, you’re getting a new Lightning adapter. If you have devices which work with the old Apple dock connector, in many cases an adapter won’t work purely because of the physical layout, not to mention the iPhone 5’s new dimensions. So in this case, my request is simple… Apple, offer your new connector up as a new standard, available for the industry to move to. It does seem to have some keen advantages and it would be nice to have everyone on the same page, widening the availability of third party add-ons that work with any phone, as opposed to having to pick one ecosystem or the other.

That’s all for me. There are other things that could be mentioned (NFC anyone?) but for me personally, this much and I’d be a very happy camper. That’s not to say the iPhone 5 isn’t a nice phone as is. Sure it is. But it could just be that much nicer. Tim? Are you taking requests?

cscreen for Intel

Years ago (2004?) I wrote a little command line utility that allowed users to alter their screen resolutions, bit depths and refresh rates. To be entirely honest I don’t even remember why I did it in the first place. However it seems to have been somewhat useful to a handful of folks over the years. I still see requests for it and occasionally get emails asking if I’ve updated cscreen for the Intel machines. The answer, now, is yes. I even created a page for cscreen for OS X so folks can link to it as needed.

That said… I do not have a Retina display and do not know anyone with a Retina display, so I cannot test it on such devices. If you have a Retina display and wish to give it a whirl, be my guest. In fact, if anyone wants to provide any feedback, please don’t hesitate.

Enjoy!

Unity Hinge Joint Tutorial Project

In the process of getting started on Centripetal 2, I ended up needing to make use of Hinge Joints. I’ve put together a Hinge Tutorial Project project with a sequence of simple scenes that might help explain and demonstrate the various options associated with Hinge Joints. Enjoy!