Skip to content

Instantly share code, notes, and snippets.

@chrisa
Created March 27, 2013 14:59
Show Gist options
  • Select an option

  • Save chrisa/5254871 to your computer and use it in GitHub Desktop.

Select an option

Save chrisa/5254871 to your computer and use it in GitHub Desktop.
#!/opt/chef-server/embedded/lib/erlang/bin/escript
-define(SELF, 'me@127.0.0.1').
-define(ERCHEF, 'erchef@127.0.0.1').
-define(ERCHEF_COOKIE, 'erchef').
-define(DEFAULT_KEY_PATH, '/etc/chef-server').
%% File 00600 mode permission specified the Erlang way. See
%% file:write_file_info for details.
-define(OWNER_RW, 8#00400 + 8#00200).
-define(OSC_ORG_ID, <<"00000000000000000000000000000000">>).
-define(CHEF_SKED_AUTHZ_ID, <<"00000000000000000000000000005ced">>).
-record(chef_user, {
'id', %% guid for object (unique)
'authz_id', %% authorization guid (placeholder - not used)
'username', %% username
'email', %% email - left null
'public_key', %% public key - might be null
'hashed_password', %% password
'salt', %% password salt
'hash_type', %% hash used to scramble password
'last_updated_by', %% authz guid of last actor to update object -
%% it is a place holder in this case
'created_at', %% time created at
'updated_at', %% time updated at
'external_authentication_uid', %% External UID, such as LDAP - nullable
'recovery_authentication_enabled', %% not used, will be null
'admin' %% if the user is an admin
}).
main([Op, Name, HashedPassword, Salt, HashType]) ->
init_network(),
case Op of
"create" ->
create_user(Name, HashedPassword, Salt, HashType);
"update" ->
update_user(Name, HashedPassword, Salt, HashType);
_ ->
io:format("first arg must be create or update~n")
end.
init_network() ->
net_kernel:start([?SELF, longnames]),
erlang:set_cookie(node(), ?ERCHEF_COOKIE),
pong = net_adm:ping(?ERCHEF).
create_user(Name, HashedPassword, Salt, HashType) ->
DbContext = make_context(),
Id = make_org_prefix_id(?OSC_ORG_ID, Name),
User = #chef_user{id = Id,
authz_id = Id,
username = Name,
email = "",
hashed_password = HashedPassword,
salt = Salt,
hash_type = HashType,
public_key = "",
external_authentication_uid = null, %% Not used in open source user
recovery_authentication_enabled = false, %% Not used in open source user
admin = true
},
Args = [User, DbContext, ?CHEF_SKED_AUTHZ_ID],
Result = rpc:call(?ERCHEF, chef_db, create, Args),
process_create_result(Name, Result).
process_create_result(Name, Result) ->
case Result of
ok ->
io:format("user ~p created.~n", [Name]),
{ok, created};
{conflict, _} ->
io:format("user ~p already exists~n", [Name]),
halt(2);
Error ->
io:format("error creating user ~p: ~p~n", [Name, Error]),
halt(2)
end.
update_user(Name, HashedPassword, Salt, HashType) ->
DbContext = make_context(),
FetchArgs = [DbContext, Name],
User = rpc:call(?ERCHEF, chef_db, fetch_user, FetchArgs),
case User of
not_found ->
io:format("user ~p not found.~n", [Name]);
{error,Error} ->
io:format("error retrieving user ~p: ~p~n", [Name, Error]);
_ ->
UpdatedUser = User#chef_user{hashed_password=HashedPassword, salt=Salt, hash_type=HashType},
UpdateArgs = [DbContext, UpdatedUser, ?CHEF_SKED_AUTHZ_ID],
Result = rpc:call(?ERCHEF, chef_db, update, UpdateArgs),
process_update_result(Name, Result)
end.
process_update_result(Name, Result) ->
case Result of
ok ->
io:format("user ~p updated.~n", [Name]),
{ok, updated};
Error ->
io:format("error updating user ~p: ~p~n", [Name, Error]),
halt(2)
end.
%% Get a DB context object
make_context() ->
Args = [make_req_id()],
rpc:call(?ERCHEF, chef_db, make_context, Args).
%% Create a binary string used as the request identifier.
make_req_id() ->
TS = os:timestamp(),
Rand = base64:encode(crypto:rand_bytes(6)),
{{Year, Month, Day}, {Hour, Minute, Second}} = calendar:now_to_universal_time(TS),
iolist_to_binary(io_lib:format("~s-~4w-~2..0w-~2..0w-~2..0w:~2..0w:~2..0w-~s",
[?MODULE,
Year, Month, Day,
Hour, Minute, Second,
Rand])).
make_org_prefix_id(OrgId, Name) ->
%% assume couchdb guid where trailing part has uniqueness
<<_:20/binary, OrgSuffix:12/binary>> = OrgId,
Bin = iolist_to_binary([OrgId, Name, crypto:rand_bytes(6)]),
<<ObjectPart:80, _/binary>> = crypto:md5(Bin),
iolist_to_binary(io_lib:format("~s~20.16.0b", [OrgSuffix, ObjectPart])).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment